使用 python 和 Opencv2 检测边缘

detect edge using python and Opencv2

我有下面这张照片,我想把它分成两部分,这样我就可以单独处理每个部分,我添加了那些黑点,让我的算法检测到这些点并将部分分开,但它检测到所有纸张的元素它'没有像我预期的那样工作,我不知道为什么

这是我的代码

import cv2
import numpy as np

image = cv2.imread('18936-model-1.jpg')

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (23, 23), 0)
cv2.imshow("blurred", blurred)
canny = cv2.Canny(blurred, 30, 30)
cnts, _ = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
print(f"Contours in the image, %d" % (len(cnts)))
shape = image.copy()
cv2.drawContours(shape, cnts, -1, (0, 255, 0), 2)
cv2.imwrite('{}.png'.format('sunflower'), shape)
cv2.imshow("Edges", shape)
cv2.waitKey(0)

这是原图

这就是我想要分开照片的方式

这就是我得到的

这是一个可能的解决方案。这个想法只是使用水平线来分割图像。然而,我们并不真的需要完整的图像。该线沿整个宽度延伸,所以我只提取了几列第一列,将它们缩减为一列并搜索存在最大强度值的位置(或索引)。

获得此信息后,我可以沿该参考分割原始图像。来看代码:

# Imports
import numpy as np
import cv2

# Image path
path = "D://opencvImages//"
fileName = "xIe0M.jpg"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# To grayscale:
grayImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

# Otsu Threshold:
_, binaryImage = cv2.threshold(grayImage, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)

# Apply thinning:
binaryImage = cv2.ximgproc.thinning(binaryImage, None, 1)

# Show the image:
showImage("binaryImage", binaryImage)

# Get image dimensions:
(imageHeight, imageWidth) = binaryImage.shape[:2]

# Get ROIs:
roiHeight = imageHeight
roiWidth = int(0.01 * imageWidth)

# Store ROIs here:
verticalCropPoints = []

# Crop first and last row:
horizontalMargin = 2
binaryImage = binaryImage[0:imageHeight, horizontalMargin:imageWidth - horizontalMargin]

# Extract left and right ROIs:
# Set Cropping dimensions:
xStart = clamp((imageWidth - roiWidth), 0, imageWidth)
xEnd = roiWidth
yStart = 0
yEnd = roiHeight

# Crop the ROI:
currentRoi = binaryImage[yStart:yEnd, xStart:xStart + xEnd]

# Image reduction to a column:
reducedImage = cv2.reduce(currentRoi, 1, cv2.REDUCE_MAX)

第一步涉及将图像转换为二进制,然后应用细化。此操作会将水平宽度标准化为一个像素。我真的不需要超过一个像素的水平线。这是目前的结果:

这是非常微妙的,但这是将 ROI(原始宽度的 1%)减少到只有一列。让我们找出最大值在哪里(你实际上可以在过去的图像上看到一个微弱的白色像素):

# Get indices of maximum values:
maxIndexCol = np.nonzero(reducedImage)

# Store all the indices here:
verticalCropPoints = np.append(verticalCropPoints, maxIndexCol[0])

现在我有了水平线的垂直位置。让我们根据这些信息进行裁剪。该方法是通过一个循环来实现的,该循环迭代地裁剪直到到达最后一个垂直坐标:

# Crop image:
cropImage = True
cropCounter = len(verticalCropPoints)

# Loop variables:
xStart = 0
xEnd = imageWidth
yPast = 0
j = 0

while cropImage:

    if j < cropCounter:
        y = int(verticalCropPoints[j])
        yStart = yPast
        yEnd = y
        yPast = y
    else:
        yStart = yPast
        yEnd = imageHeight
        cropImage = False

    currentCrop = inputImage[yStart:yEnd, xStart:xStart + xEnd]
    cv2.imshow("currentCrop", currentCrop)
    cv2.waitKey(0)

    # Increment step counter:
    j += 1

这些是结果(针对 post 进行缩放,在新选项卡中打开它们以全分辨率查看):

让我们看看该算法如何扩展到一条以上的水平线。这是对您的原始图像的修改,带有两条水平线:

产生这些单独的图像:

我使用了一个辅助函数,将数字限制在有效范围内:

# Clamps an integer to a valid range:
def clamp(val, minval, maxval):
    if val < minval: return minval
    if val > maxval: return maxval
    return val

您还需要 OpenCV 的扩展图像处理 模块才能 skeletonize/thin 图像。