Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

contour/object detection in OpenCV

I have some experience of OpenCV, but really having problems detecting contours in the images below. I am able to detect some of the black circles shown, but these are dirt on the sensor, and I really want to detect the tracks in the images, as outlined in red in the second image.

Image

Image with tracks required to be detected outlined in red

I'm trying with Canny Edge detection, and with cv.ADAPTIVE_THRESH_GAUSSIAN_C, which gives the best results, but it's very difficult to get the correct parameters, and results vary from image to image. I have also tried with denoising using cv.fastNlMeansDenoising, also Gaussian blurring before Canny Edge detection, but results are not much improved.

It's relatively easy to detect the drops, but not the tracks.

I may have to write something just to try to detect the tracks by scanning in the image line by line, but this seems very clunky, and I'm not sure how to start this. I'm using OpenCV with Python.

Does anyone have any suggestions?

Edit: Posting raw image as suggested by @Rethunk Raw Image:

This is the raw image

Cropped section of raw image after using CLAHE Section of raw image cropped after using CLAHE

I am using CLAHE for contrast enhancement as follows:

def inc_contrast(img,  cLimit=100.0, tGridSize=21):
    # converting to LAB color space
    lab = cv.cvtColor(img, cv.COLOR_BGR2LAB)
    l_channel, a, b = cv.split(lab)

    # Applying CLAHE to L-channel
    clahe = cv.createCLAHE(clipLimit=cLimit, tileGridSize=(tGridSize, tGridSize))
    cl = clahe.apply(l_channel)

    # merge the CLAHE enhanced L-channel with the a and b channel
    limg = cv.merge((cl, a, b))

    # Converting image from LAB Color model to BGR color spcae
    enhanced_img = cv.cvtColor(limg, cv.COLOR_LAB2RGB)
    return enhanced_img

This is the resulting image after using despeckling and difference of gaussians in GIMP after saving the CLAHE processed image:

Section of Image after despeckling & dog

To get this image in GIMP I use the following:

Despeckling in GIMP (Filter/Enhance/Despeckle) Radius 3, Black level 7, White level 248.

Difference of Gaussians in GIMP (Filter.Edge Detection/Difference of Gaussians) Radius 1 10, Radius 2 4.2, Opacity 100.

I am trying to run these commands from the python console within GIMP, but it's not straightforward, ideally would like to do all processing in python/openCV.

like image 832
Jennifer B Avatar asked Feb 04 '26 15:02

Jennifer B


1 Answers

I'm not sure if I should be posting this as an answer or not. This is largely copied from my answer in (Detecting line from noisy image). The base idea is the same of convolving a line kernel over the image to highlight lines. If this is too much of a repeat I can delete this answer and just leave a comment linking back to the other answer.

There are three tweakable parameters:

  • klength -> the minimum length of the lines you want to detect (lower = more sensitive)
  • kwidth -> the minimum width of the lines you want to detect (lower = more sensitive)
  • threshold -> the minimum response level to mark as positive (lower = more sensitive)

The current settings get two extra little dots in the image, those could be filtered out by size (opencv contour area) if you want.

enter image description here

enter image description here

import cv2
import math
import numpy as np

# returns integer point positions with 0.5 ties-to-even rule
def intify(point):
    point[0] = round(point[0]);
    point[1] = round(point[1]);
    return point

# translate 2d point in-place
def translate2D(point, shift, sign=1):
    x = point[0] + (shift[0] * sign)
    y = point[1] + (shift[1] * sign)
    point[0] = x
    point[1] = y

# rotate a 2d point in-place
def rotate2D(point, angle_deg, anchor=[0,0]):
    rads = math.radians(angle_deg);
    translate2D(point, anchor, sign=-1)
    x,y = point
    rx = x * math.cos(rads) - y * math.sin(rads)
    ry = x * math.sin(rads) + y * math.cos(rads)
    point[0] = rx
    point[1] = ry
    translate2D(point, anchor, sign=1)

# moves a point to a new origin frame
def pointToFrame(point, pos, angle, no_round=False):
    rotate2D(point, angle)
    translate2D(point, pos)
    if not no_round:
        intify(point)

# rescale image
def scale(img, scale_factor):
    h,w = img.shape[:2];
    h = int(h*scale_factor);
    w = int(w*scale_factor);
    return cv2.resize(img, (w,h));

# chops padding
def unpad(img, padsize):
    h,w = img.shape[:2];
    img = img[padsize:h-padsize, padsize:w-padsize];
    return img;

def build_kernel(angle, ksize, width):
    kernel = np.zeros((ksize,ksize), np.float32)

    # get line endpoints
    middle = int(ksize / 2)
    center = [middle, middle]
    start = [-ksize, 0]
    end = [ksize, 0]
    pointToFrame(start, center, angle)
    pointToFrame(end, center, angle)

    # draw the masking line
    kernel = cv2.line(kernel, start, end, 1, width)
    kernel /= np.sum(kernel) # normalize
    return kernel

# load image
filepath = "deleteme_noisy_img.png";
img = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE)
orig_height, orig_width = img.shape[:2]

# pad to avoid edge dropoff
pad = int(orig_width/2)
img = cv2.copyMakeBorder(img, pad, pad, pad, pad, cv2.BORDER_REFLECT, None)

# get mask
_, mask = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
height,width = mask.shape

# background canvas
canvas = np.zeros_like(mask)

# tweakable settings
klength = 71
kwidth = 5
threshold = 180

# draw line
angle = 0
filenum = 0
while angle < 180:
    # create a line kernel
    kernel = build_kernel(angle, klength, kwidth)
    angle += 1;

    # convolve
    conv = cv2.filter2D(mask, -1, kernel, borderType=cv2.BORDER_CONSTANT)

    # paint onto canvas
    _, linemask = cv2.threshold(conv, threshold, 255, cv2.THRESH_BINARY) # tweakable sensitivity setting
    canvas = cv2.bitwise_or(canvas, linemask)

    # chop off padding
    display_canvas = unpad(canvas, pad)
    display_conv = unpad(conv, pad)

    # stack the images
    sbs = np.hstack((display_canvas, display_conv))
    sbs = scale(sbs, 0.5)

    # show
    cv2.imshow("side-by-side", sbs)
    key = cv2.waitKey(1)
    if key == ord('q'):
        break

    # save to folder for gif
    # filename = "images/" + str(filenum).zfill(5) + ".png"
    # cv2.imwrite(filename, sbs)
    # filenum += 1
cv2.destroyAllWindows()

# draw on image
display_canvas = unpad(canvas, pad)
orig_img = cv2.imread(filepath)
orig_img[display_canvas == 255] = (0,0,200)

# show image
cv2.imshow("Image", orig_img)
cv2.imshow("Canvas", display_canvas)
# cv2.imwrite("red.png", orig_img)
cv2.waitKey(0)
like image 147
Ian Chu Avatar answered Feb 06 '26 04:02

Ian Chu