Search code examples
pythonopencvdetection

Opencv rectangle detection on noisy image


One question, is it possible to dectect rectangle on image when it touch noise lines and other shapes This is my function to detect contoures on image:

def findContours(img_in):
w, h, c = img_in.shape  # img_in is the input image
resize_coeff = 0.25
img_in = cv2.resize(img_in,(int(resize_coeff * h), int(resize_coeff * w)))
img_in = ip.findObjects(img_in)




blr = cv2.GaussianBlur(img_in, (9, 9), 0)
img = cv2.Canny(blr, 50, 250, L2gradient=False)

kernel = np.ones((5, 5), np.uint8)
img_dilate = cv2.dilate(img, kernel, iterations=1)
img = cv2.erode(img_dilate, kernel, iterations=1)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
max_index, max_area = max(enumerate([cv2.contourArea(x) for x in contours]), key=lambda x: x[1])
max_contour = contours[max_index]
img_out = cv2.resize(img, (int(resize_coeff * h), int(resize_coeff * w)))
cv2.drawContours(img_in, [max_contour], 0, (0, 0, 255), 2)
re.rectangle(img, [max_contour])
cv2.imshow("test",img_in)
cv2.imshow("test1",img)

cv2.waitKey()
return img

I got this result: enter image description here

The result I want: enter image description here

When I use shape detecion I got result that it have 15 angles and not four. Function:

def rectangle(img, contours):
for contour in contours:
    approx = cv2.approxPolyDP(contour, 0.01 * cv2.arcLength(contour, True), True)
    print(len(approx))
    x = approx.ravel()[0]
    y = approx.ravel()[1] - 5

    if len(approx) == 4:
        print("Rect")
        x, y, w, h = cv2.boundingRect(approx)
        aspectRatio = float(w) / h
        print(aspectRatio)
        cv2.putText(img, "rectangle", (x, y), cv2.FONT_HERSHEY_COMPLEX, 0.5, (0, 0, 0))

EDIT: Original image: enter image description here


Solution

  • What if you can remove noise around that shape? I think your mask is good for more processing:

    import numpy as np
    import sys
    import cv2
    
    # Load the mask
    dir = sys.path[0]
    im = cv2.imread(dir+'/img.png')
    H, W = im.shape[:2]
    
    # Make gray scale image
    gry = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    
    # Make binary image
    bw = cv2.threshold(gry, 127, 255, cv2.THRESH_BINARY)[1]
    bw = ~bw
    
    # Focuse on edges
    bw = cv2.erode(bw, np.ones((5, 5)))
    
    # Use flood fill to remove noise
    cv2.floodFill(bw, np.zeros((H+2, W+2), np.uint8), (0, 0), 0)
    bw = cv2.medianBlur(bw, 7)
    
    # Remove remained noise with another flood fill
    nonRectArea = bw.copy()
    cv2.floodFill(nonRectArea, np.zeros((H+2, W+2), np.uint8), (W//2, H//2), 0)
    bw[np.where(nonRectArea == 255)] = 0
    
    # Find contours and sort them by width
    cnts, _ = cv2.findContours(bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    cnts.sort(key=lambda p: cv2.boundingRect(p)[2], reverse=True)
    
    # Find biggest blob
    x, y, w, h = cv2.boundingRect(cnts[0])
    cv2.rectangle(im, (x, y), (x+w, y+h), 127, 1)
    
    # Save output
    cv2.imwrite(dir+'/img_1.png', im)
    cv2.imwrite(dir+'/img_2.png', bw)
    cv2.imwrite(dir+'/img_3.png', nonRectArea)
    

    enter image description here