2

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

jan
  • 49
  • 9
  • 2
    its not rectangle, its a polygon. is this helpful for you? -> https://www.geeksforgeeks.org/python-detect-polygons-in-an-image-using-opencv/ – Ghost Ops Aug 22 '21 at 13:40
  • Yes its not rectangle but it has to have 4 edges/corners. but the problem is beacuse of noise around that polygon (The lines that are sticking out) – jan Aug 22 '21 at 13:43
  • 3
    show us the original unfiltered picture – Christoph Rackwitz Aug 22 '21 at 13:51
  • images are noisy. You cannot do generally without post-processing of the contours. Hough transform is the usual answer to this kind of tasks. See [this excellent answer](https://stackoverflow.com/a/44156317/2131957) on one approach for rectangle detection. – Yuri Feldman Aug 22 '21 at 14:03

1 Answers1

2

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

Shamshirsaz.Navid
  • 2,224
  • 3
  • 22
  • 36
  • Hello, this looks great, but I have issue with floodfill. Maybe do you know issue? cv2.floodFill(bw, np.zeros((H + 2, W + 2), np.uint8), (0, 0), 0) cv2.error: OpenCV(4.5.3) C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-1i5nllza\opencv\modules\imgproc\src\floodfill.cpp:491: error: (-5:Bad argument) Number of channels in input image must be 1 or 3 in function 'cv::floodFill'. EDIT: Manage to solve it. I just remove cvtColor function.. – jan Aug 22 '21 at 16:17
  • 1
    Hello jan :) use your mask as input not your original image - I continued from where you made the black and white mask. – Shamshirsaz.Navid Aug 22 '21 at 16:21
  • 1
    @jan I used this image https://i.stack.imgur.com/IWVoX.png as input :) First lets try with this one – Shamshirsaz.Navid Aug 22 '21 at 16:23
  • 1
    Yes I did used mask, but I got this error when I used cv2.cvtColor function. So I just removed it – jan Aug 22 '21 at 16:31
  • One more problem, I used this processing on another image and there are gaps. And when you use second floodfill (NoRectArea) it remove everything. This is after first fillflood https://prnt.sc/1qenell – jan Aug 22 '21 at 16:36
  • 1
    To get a good output in image processing; It is better to have a lot of pictures and take various tests. Because when you only have one image, the algorithm is written only according to that image :) Change the parameters and check again. Comment on the codes and check again :) but let me check your image – Shamshirsaz.Navid Aug 22 '21 at 16:41
  • 1
    I checked for the new image. This method does not work at all for the second mask. One solution is to take a step back and create a better mask from the original image. I suggest you read more about the FloodFill method. This way you will understand why this method is used and you can better adapt the mask accordingly. – Shamshirsaz.Navid Aug 22 '21 at 16:49
  • 1
    okay thanks I will try to find right parameters in mask. Thank again for help :) – jan Aug 22 '21 at 18:39