使用OpenCV在Python中,如何获取检测到的轮廓的极端点?

huangapple go评论73阅读模式
英文:

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

问题

以下是翻译好的部分:

我想检测轮廓并绘制其极端点,我的问题是如何从不一定连续的检测到的轮廓中获取极端点。

我想检测以下图像的轮廓使用OpenCV在Python中,如何获取检测到的轮廓的极端点?

使用OpenCV在Python中,如何获取检测到的轮廓的极端点?
并从轮廓的角落绘制线条,如下所示
使用OpenCV在Python中,如何获取检测到的轮廓的极端点?

但我得到了以下结果:
使用OpenCV在Python中,如何获取检测到的轮廓的极端点?

如何获取四个轮廓的角落以绘制它们?
这是我尝试过的内容:

#!/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)
英文:

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 使用OpenCV在Python中,如何获取检测到的轮廓的极端点?

使用OpenCV在Python中,如何获取检测到的轮廓的极端点?
and draw lines from the contour corners as follow
使用OpenCV在Python中,如何获取检测到的轮廓的极端点?

But I get the following:
使用OpenCV在Python中,如何获取检测到的轮廓的极端点?

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)

答案1

得分: 3

以下是翻译的代码部分:

# 我们可以找到每个轮廓的最小面积矩形,并标记点为矩形的垂直边之间的中心点。

# 我们必须假设轮廓在水平方向上(允许小角度)。

# 对于过滤小轮廓(视为噪声),我们可以找到轮廓面积,并将最小面积限制为100像素。

# 找到极端点(作为中心点或边缘)并不是那么简单,因为`cv2.minAreaRect`和`cv2.boxPoints(rect)`不会对点进行排序。

# 我们可能需要检查框的角度,如果框是垂直的(如果框的角度是垂直的),则旋转框90度:

rect = cv2.minAreaRect(c)  # 为了找到线宽,找到最小面积矩形
cx, cy = rect[0]
w, h = rect[1]
alpha = rect[2]

# 如果线是垂直的,将框旋转90度(这可能不是最佳解决方案...)
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
extLeft = (int(x0), int(y0))
extRight = (int(x1), int(y1))

# 如果需要,交换左右点:

if x0 > x1:
    extLeft, extRight = extRight, extLeft  # 交换左右
# 更新后的代码示例:

#!/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)  # 计算面积
    if area > 100:  # 假设面积大于100的轮廓有效
        rect = cv2.minAreaRect(c)  # 为了找到线宽,找到最小面积矩形
        cx, cy = rect[0]
        w, h = rect[1]
        alpha = rect[2]

        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
        extLeft = (int(x0), int(y0))
        extRight = (int(x1), int(y1))

        if x0 > x1:
            extLeft, extRight = extRight, extLeft  # 交换左右

        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()
# 输出图像:

[![enter image description here][1]][1]
# 我们也可以使用三角学:

#!/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)  # 计算面积
    if area > 100:  # 假设面积大于100的轮廓有效
        rect = cv2.minAreaRect(c)  # 为了找到线宽,找到最小面积矩形
        cx, cy = rect[0]
        w, h = rect[1]
        alpha = rect[2]

        if alpha > 180:
            alpha -= 360

        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()


<details>
<summary>英文:</summary>

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&#39;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&#39;s probably not the best solution...)
        if np.abs(alpha) &gt; 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 &gt; 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(&quot;img.png&quot;)
    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 &gt; 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&#39;s probably not the best solution...)
            if np.abs(alpha) &gt; 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 &gt; 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(&quot;Image&quot;, image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

---

Output image:  

[![enter image description here][1]][1]

---

We may also use trigonometry:  

    #!/usr/bin/env python
    import numpy as np
    import cv2
    
    image = cv2.imread(&quot;img.png&quot;)
    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 &gt; 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 &gt; 180:
                alpha -= 360
    
            # Rotate the box by 90 degrees if line is vertical (it&#39;s probably not the best solution...)
            if np.abs(alpha) &gt; 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(&quot;Image&quot;, image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

---

Output of the other sample image:  

[![enter image description here][2]][2]


  [1]: https://i.stack.imgur.com/TaVMI.png
  [2]: https://i.stack.imgur.com/cpuIj.png

</details>



huangapple
  • 本文由 发表于 2023年5月31日 23:41:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76375225.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定