Zakkkk
Zakkkk

Reputation: 33

Detecting the edge of a colorful picture OpenCV

I am new to CV and I just learned how to detect the edge of a paper. I want to try something more complicated. So I make a screenshot from a movie website and want to detect the poster from the website. It works well if the background color is different from the poster. But when they are similar in color, I can't find the edge of the picture by cv2.findContours() The original Picture is: Poster

And what I do is:

img = cv2.imread('pic5.jpg')
orig = img.copy()
image = orig
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
binary = cv2.medianBlur(gray,3)
# blur = cv2.GaussianBlur(binary, (5, 5), 0)
# ret, binary = cv2.threshold(blur,127,255,cv2.THRESH_TRUNC)
edged = cv2.Canny(binary, 3, 30)
show(edged)

# detect edge
contours, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = sorted(contours, key=cv2.contourArea, reverse=True)[:5]

#
for c in cnts:
    # approx
    peri = cv2.arcLength(c, True)
    eps = 0.02
    approx = cv2.approxPolyDP(c, eps*peri, True)

    # detect square (4 points)
    if len(approx) == 4:
        screenCnt = approx
        break

res = cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
show(orig)

And the result is: after preprocess What I detect

I don't know if this method works. Is it possible to detect the square part based on the background color (regardless of the poster's color)?

Upvotes: 3

Views: 1319

Answers (1)

Rotem
Rotem

Reputation: 32084

You may continue with the edged result, and use closing morphological operation for closing small gaps.

Instead of searching for a rectangle using approxPolyDP, I suggest you to find the bounding rectangle of the largest connected component (or largest contour).

In my code sample, I replaced findContours with connectedComponentsWithStats due to the external boundary line.
You may use opening morphological operation to get rid of the external line (and use continue using findContours).

You may also use approxPolyDP for refining the result.


Here is the code sample:

import numpy as np
import cv2

img = cv2.imread('pic5.png')
orig = img.copy()
image = orig
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
binary = cv2.medianBlur(gray, 3)
edged = cv2.Canny(binary, 3, 30)

edged = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, np.ones((5,5)))  # Close small gaps

#contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
#c = max(contours, key=cv2.contourArea) # Get the largest contour
#x, y, w, h = cv2.boundingRect(c)  # Find bounding rectangle.

nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(edged, 8)  # finding components

# https://stackoverflow.com/a/61662694/4926757
# Find the largest non background component.
# Note: range() starts from 1 since 0 is the background label.
max_label, max_size = max([(i, stats[i, cv2.CC_STAT_AREA]) for i in range(1, nb_components)], key=lambda x: x[1])

# Find bounding rectangle of largest connected component.
x = stats[max_label, cv2.CC_STAT_LEFT]
y = stats[max_label, cv2.CC_STAT_TOP]
w = stats[max_label, cv2.CC_STAT_WIDTH]
h = stats[max_label, cv2.CC_STAT_HEIGHT]

res = image.copy()
cv2.rectangle(res, (x, y), (x+w, y+h), (0, 255, 0), 2)  # Draw a rectangle

cv2.imshow('edged', edged)
cv2.imshow('res', res)
cv2.waitKey()
cv2.destroyAllWindows()

Results:

edged:
enter image description here

res:
enter image description here

Upvotes: 7

Related Questions