英文:
Using OpenCV in Python, how do I get the extreme point of a distinct detected contour?
问题
以下是翻译好的部分:
我想检测轮廓并绘制其极端点,我的问题是如何从不一定连续的检测到的轮廓中获取极端点。
如何获取四个轮廓的角落以绘制它们?
这是我尝试过的内容:
#!/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
and draw lines from the contour corners as follow
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'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][1]][1]
---
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][2]][2]
[1]: https://i.stack.imgur.com/TaVMI.png
[2]: https://i.stack.imgur.com/cpuIj.png
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论