Sennsei
Sennsei

Reputation: 61

Detecting edge lines in a detailed image containing multiple elements using opencv

I have the following image of a seedling tray, and I am attempting to detect the tray edges. Seedling Tray

When I attempt to run a Canny edge detection - after applying a Gaussian blur - this is the result that is provided:

Canny

Running a probabilistic Hough transform results in a jumbled mess like so: Hough Transform

Are there any opencv functions that should be used instead due to the detail and level of objects present in the image? Should I use a different program entirely? Here is an example of what I would hope the output would achieve: enter image description here

Upvotes: 0

Views: 356

Answers (1)

M Virts
M Virts

Reputation: 173

import cv2 as cv
import numpy as np
import math

def removebg(x,s):
    # return x - cv.GaussianBlur(x,(s,s),0)
    bi = cv.medianBlur(x,s).astype(np.float32)
    return x - (bi.astype(np.float32) - np.mean(bi))

def threshold_and_hough_lines(x,thresh):
    # x = (x > 10) *255
    x = x.astype(np.uint8)
    return cv.HoughLines(x,1,np.pi/360,thresh)

def outliers(x,factor=1):
    l,median,u = np.quantile(x,[.25,.5,.75])
    iqd = u-l
    _,thresh  = cv.threshold(x,median+factor*iqd,np.max(x),cv.THRESH_BINARY)
    return thresh

img = cv.imread("/data/stuff/seeds.jpeg")

print(img.shape)

draw_len=max(img.shape)*2

img_rembg = removebg(img,201)

cv.imwrite('img_rembg.jpg',img_rembg)

gray = np.mean(img_rembg,axis=2)

cv.imwrite('gray.jpg',gray)

thresh = outliers(gray)

cv.imwrite('thresh.jpg',thresh)

structure_element = np.ones((35,1))
structure_element = structure_element.astype(np.uint8)

iters=1
vlines = cv.morphologyEx(thresh, cv.MORPH_OPEN, structure_element,iterations=iters)
hlines = cv.morphologyEx(thresh, cv.MORPH_OPEN, structure_element.T,iterations=iters)

cv.imwrite('vlines.jpg',vlines)
cv.imwrite('hlines.jpg',hlines)

hough_thresh=int(math.sqrt(img.shape[0]*img.shape[1])*.3)
print(hough_thresh)

linesh = threshold_and_hough_lines(hlines,hough_thresh)
linesv = threshold_and_hough_lines(vlines,hough_thresh)

for line in (*linesh,*linesv):
    rho,theta = line[0]
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + draw_len*(-b))
    y1 = int(y0 + draw_len*(a))
    x2 = int(x0 - draw_len*(-b))
    y2 = int(y0 - draw_len*(a))
    cv.line(img,(x1,y1),(x2,y2),(0,0,255),2)
cv.imwrite('combined.jpg',img)

I updated my method with some pre-processing to flatten the image ("removebg") as well as morphological operations (open and close) with structual elements matching a vertical bar and horizontal bar separately to enhance the planter edges after thresholding. Most of the parameters can be tuned for different size objects and images, which was the main problem going from the small resolution photo to full resolution. The main parameter of HoughLines is the detection threshold, which I put together a rough calculation based on the size of the image that seems to work well. This code seems to work with both small and large versions of the example image.

The first thing I would mess with to get those missing horizontal lines is the multiplyer on hough_thresh (set to .3 in this post). Lower = more lines detected.

I explored some convolution based methods and I recommend taking a look at them. Trying to match the shape of each planter or maybe corners may be better in the long run if you consistently have images with obscured lines.

Results

Upvotes: 2

Related Questions