Using OpenCV in Python, how do I get the extreme point of a distinct detected contour?

appie

I want to detect a contour and draw its extreme points, my problem is how to get the extreme points from a distinct detected contour as the shape is not always continuous.

I want to detect the contour of the following image enter image description here

enter image description here and draw lines from the contour corners as follow enter image description here

But I get the following: enter image description here

How can I get the four contour corners to draw them? That's what I have tried:

#!/usr/bin/env python
import numpy as np
import cv2

image = cv2.imread("img.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)

thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)

ocv = cv2.ximgproc.thinning(thresh,20)

cnts = cv2.findContours(ocv.copy(), cv2.RETR_EXTERNAL,
    cv2.CHAIN_APPROX_SIMPLE)

cnts = cnts[0]
c = max(cnts, key=cv2.contourArea)
extLeft = tuple(c[c[:, :, 0].argmin()][0])
extRight = tuple(c[c[:, :, 0].argmax()][0])

cv2.drawContours(image, cnts, -1, (0, 255, 255), 2)
cv2.line(image, extLeft, extRight, (255,0,0), 2)
cv2.circle(image, extLeft, 8, (0, 0, 255), -1)
cv2.circle(image, extRight, 8, (0, 255, 0), -1)
cv2.imshow("Image", image)
cv2.waitKey(0)
Rotem

We may find minimum area rectangle of each contour, and mark the points as the center point between each of the vertical edges of the rectangle.

We have to assume that the contours are in horizontal pose (small angle is allowed).


For filtering small contours (considered to be noise), we may find contour area and limit the minimum area to say 100 pixels.

Finding the extreme points (as centers if edges) is not so trivial, because the cv2.minAreaRect and cv2.boxPoints(rect) don't sort the points.

  • We may have to check the angle of the box, and rotate the box by 90 degrees if box is vertical (if box angle is vertical):

     rect = cv2.minAreaRect(c)  # Find minimum area rectangle for finding the line width
     cx, cy = rect[0]
     w, h = rect[1]
     alpha = rect[2]
    
     # Rotate the box by 90 degrees if line is vertical (it's probably not the best solution...)
     if np.abs(alpha) > 45:
         rect = ((cx, cy), (h, w), 90 - alpha)
    
  • Find the extreme points as the centers of the vertical edges:

     box = cv2.boxPoints(rect)
     x0 = (box[0, 0] + box[1, 0])/2
     y0 = (box[0, 1] + box[1, 1])/2
     x1 = (box[2, 0] + box[3, 0])/2
     y1 = (box[2, 1] + box[3, 1])/2
     extLeft = (int(x0), int(y0))
     extRight = (int(x1), int(y1))
    
  • Swap left and right points if required:

     if x0 > x1:
         extLeft, extRight = extRight, extLeft  # Swap left and right
    

Updated code sample:

#!/usr/bin/env python
import numpy as np
import cv2

image = cv2.imread("img.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)

thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)

#ocv = cv2.ximgproc.thinning(thresh,20)

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

for c in cnts:
    area = cv2.contourArea(c)  # Compute the area
    if area > 100:  # Assume area above 100 applies valid contour.
        rect = cv2.minAreaRect(c)  # Find minimum area rectangle for finding the line width
        cx, cy = rect[0]
        w, h = rect[1]
        alpha = rect[2]

        # Rotate the box by 90 degrees if line is vertical (it's probably not the best solution...)
        if np.abs(alpha) > 45:
            rect = ((cx, cy), (h, w), 90 - alpha)

        box = cv2.boxPoints(rect)
        x0 = (box[0, 0] + box[1, 0])/2
        y0 = (box[0, 1] + box[1, 1])/2
        x1 = (box[2, 0] + box[3, 0])/2
        y1 = (box[2, 1] + box[3, 1])/2
        #cv2.line(image, (int(x0), int(y0)), (int(x1), int(y1)), (0, 255, 0))  # Draw the line for testing
        #cv2.drawContours(image,np.int0(box),0,(0,0,255),2)

        extLeft = (int(x0), int(y0))
        extRight = (int(x1), int(y1))

        if x0 > x1:
            extLeft, extRight = extRight, extLeft  # Swap left and right

        cv2.circle(image, extLeft, 8, (0, 0, 255), -1)
        cv2.circle(image, extRight, 8, (0, 255, 0), -1)

        
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Output image:

enter image description here


We may also use trigonometry:

#!/usr/bin/env python
import numpy as np
import cv2

image = cv2.imread("img.png")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)

thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations=2)
thresh = cv2.dilate(thresh, None, iterations=2)

cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

for c in cnts:
    area = cv2.contourArea(c)  # Compute the area
    if area > 100:  # Assume area above 100 applies valid contour.
        rect = cv2.minAreaRect(c)  # Find minimum area rectangle for finding the line width
        cx, cy = rect[0]
        w, h = rect[1]
        alpha = rect[2]

        if alpha > 180:
            alpha -= 360

        # Rotate the box by 90 degrees if line is vertical (it's probably not the best solution...)
        if np.abs(alpha) > 45:
            alpha = 90 - alpha
            rect = ((cx, cy), (h, w), alpha)

        w, h = rect[1]
        alpha = np.deg2rad(alpha)
        x0 = cx - w/2*np.cos(alpha)
        y0 = cy - h/2*np.sin(alpha)
        x1 = cx + w/2*np.cos(alpha)
        y1 = cy + h/2*np.sin(alpha)
        
        extLeft = (int(x0), int(y0))
        extRight = (int(x1), int(y1))

        cv2.circle(image, extLeft, 8, (0, 0, 255), -1)
        cv2.circle(image, extRight, 8, (0, 255, 0), -1)
        
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

Output of the other sample image:

enter image description here

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Extracting the coordinates from a single contour detected by the findcontours in opencv using python

OpenCV Get the closest contour to a point

how do i access contour sequentially in opencv

Opencv/python - how can i get the coordinates of detected areas after image processing

Using Python, how do I add only distinct messages to a list?

How can I display the total number of circles detected in OpenCV? (Python)

How can I draw two contours on an image given a contour distance of 100 pixels between them using python and opencv

OpenCV Python, remove shadow and get contour

How do I get distinct grouped data?

python opencv crop using contour hierarchy

Area of a closed contour on a plot using python openCV

OpenCV: How to get the bounding box for each contour?

How to get an expanded or contracted contour in OpenCV?

How to get continuous smooth/average of contour in OpenCV?

Getting contour's Point using opencv.js

How to fill contour line using opencv?

How do I make rectangular image squared using OpenCV and Python?

How do I display the contours of an image using OpenCV Python?

How do I crop the black background of the image using OpenCV in Python?

How can I get the largest contour out of an openCV function so that I can store it in an array

How do I uninstall Extreme Download Manager?

How to complete/close a contour in python opencv?

How do I get the probability value of a "detected" object?

How can I get Coordinates of Points of Contour corners in OpenCV.js?

Contour Identification using OpenCV

How to save detected face in Android using OpenCV?

How do I get a curve point on a line?

how do I get a point between the numbers?

How can I reliably choose only the outer contour of a maksed droplet image with python opencv?