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.


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:

Cropped section of raw image 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:

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.
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:
The current settings get two extra little dots in the image, those could be filtered out by size (opencv contour area) if you want.


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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With