使用OpenCV Python检测二进制图像中的补丁

6

我希望检测出这张图片中的所有补丁,以下是用于检测的代码:

import cv2
import numpy as np
import matplotlib.pyplot as plt


image=cv2.imread("bw2.jpg",0)

# convert to RGB
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# create a binary thresholded image
_, binary = cv2.threshold(gray, 0, 500, cv2.THRESH_BINARY_INV)
# show it
plt.imshow(gray, cmap="gray")
plt.show()
# find the contours from the thresholded image
contours, hierarchy = cv2.findContours(gray, cv2.RETR_TREE, 
cv2.CHAIN_APPROX_SIMPLE)
print("contours:",contours)
# draw all contours
for c in contours:
if cv2.contourArea(c) < 3000:
    continue

(x, y, w, h) = cv2.boundingRect(c)
#cv2.rectangle(image, (x,y), (x+w,y+h), (0, 255, 0), 2)

## BEGIN - draw rotated rectangle
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image,[box],0,(255,51,255),2)

# show the image with the drawn contours
plt.imshow(image)
#plt.imshow(im3)

cv2.imwrite("detectImg2.png",image)
plt.show()

我得到的输出图像如下enter image description here 我想检测它们中的所有内容,有人能告诉我该如何实现吗?我对图像处理不熟悉。

您不能裁剪旋转的矩形。您可以将旋转矩形外的背景设置为透明,或者将其裁剪为旋转矩形的正常边界框。请提供更多详细信息或示例输出。请参阅“如何提出好问题”(https://stackoverflow.com/help/how-to-ask)和“如何创建最小可重现示例”(https://stackoverflow.com/help/minimal-reproducible-example)中的“导览”(https://stackoverflow.com/tour)和“帮助中心”的指南(https://stackoverflow.com/help)。 - fmw42
我实际上想要裁剪这些补丁并将其直接旋转,希望您能理解。 - suji
你需要将图像裁剪到旋转矩形的边界,使背景变为白色,然后旋转以使其变直,然后再次裁剪。cv2.minAreaRect()函数将返回您需要使用的旋转角度。然后使用轮廓来查找边界,通过在旋转时使背景变为白色并获取轮廓边界框。或者获取旋转矩形的角落并相应地旋转它们,并使用旋转后的角落从角落的边界框中裁剪图像。请阅读论坛中的指南并发布您当前的代码。 - fmw42
正如你所说,我把所有信息都放在这里了,我完成了其中一半,但是图像中仍然缺少一些补丁。 - suji
我不明白你想要什么。你是想得到白色聚类的旋转矩形还是每个白色区域单独的旋转矩形?我认为是后者,但我想确保我理解正确。你是不是没有检测到所有的白色区域?也许你的面积阈值太小了? - fmw42
你能回答我另一个问题吗?https://stackoverflow.com/questions/62230063/to-find-and-rotate-a-image-from-vertical-to-horizontal-using-opencv-python?noredirect=1#comment110060749_62230063,我也找不到解决方案。 - suji
2个回答

4
以下是使用Python OpenCV提取和旋转图像中每个blob的方法:
  • 读取输入图像
  • 转换为灰度图像
  • 二值化处理
  • 应用形态学开闭运算以清除小斑点
  • 获取所有外部轮廓
  • 遍历每个轮廓并执行以下操作:
  • 在输入图像的副本上绘制轮廓
  • 获取轮廓的旋转矩形,并提取其中心、尺寸和旋转角度
  • 获取旋转矩形的四个角
  • 在另一个输入图像的副本上绘制旋转矩形
  • 校正图像反转的旋转角度
  • 生成填充旋转矩形的蒙版图像
  • 将蒙版图像应用于形态学清除后的图像,以删除附近的其他白色区域
  • 使用中心和校正后的旋转角度获取仿射变换矩阵
  • 使用warpAffine对未旋转的图像进行旋转
  • 获取未旋转图像中一个blob的轮廓
  • 获取轮廓的边界框
  • 裁剪蒙版图像(或交替裁剪输入图像)
  • 保存裁剪后的图像
  • 退出循环
  • 保存轮廓和旋转矩形图像


输入:

enter image description here

import cv2
import numpy as np

image = cv2.imread("bw2.jpg")
hh, ww = image.shape[:2]

# convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# create a binary thresholded image
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

# apply morphology
kernel = np.ones((7,7), np.uint8)
clean = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = np.ones((13,13), np.uint8)
clean = cv2.morphologyEx(clean, cv2.MORPH_CLOSE, kernel)


# get external contours
contours = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]

contour_img = image.copy()
rotrect_img = image.copy()
i = 1
for c in contours:
    # draw contour on input
    cv2.drawContours(contour_img,[c],0,(0,0,255),2)

    # get rotated rectangle from contour
    # get its dimensions
    # get angle relative to horizontal from rotated rectangle
    rotrect = cv2.minAreaRect(c)
    (center), (width,height), angle = rotrect
    box = cv2.boxPoints(rotrect)
    boxpts = np.int0(box)

    # draw rotated rectangle on copy of image
    cv2.drawContours(rotrect_img,[boxpts],0,(0,255,0),2)

    # from https://www.pyimagesearch.com/2017/02/20/text-skew-correction-opencv-python/
    # the `cv2.minAreaRect` function returns values in the
    # range [-90, 0); as the rectangle rotates clockwise the
    # returned angle tends to 0 -- in this special case we
    # need to add 90 degrees to the angle
    if angle < -45:
        angle = -(90 + angle)

    # otherwise, check width vs height
    else:
        if width > height:
            angle = -(90 + angle)

        else:
            angle = -angle

    # negate the angle for deskewing
    neg_angle = -angle

    # draw mask as filled rotated rectangle on black background the size of the input
    mask = np.zeros_like(clean)
    cv2.drawContours(mask,[boxpts],0,255,-1)

    # apply mask to cleaned image
    blob_img = cv2.bitwise_and(clean, mask)

    # Get rotation matrix
    #center = (width // 2, height // 2)
    M = cv2.getRotationMatrix2D(center, neg_angle, scale=1.0)
    #print('m: ',M)

    # deskew (unrotate) the rotated rectangle
    deskewed = cv2.warpAffine(blob_img, M, (ww, hh), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

    # threshold it again
    deskewed = cv2.threshold(deskewed, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

    # get bounding box of contour of deskewed rectangle
    cntrs = cv2.findContours(deskewed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
    cntr = cntrs[0]
    x,y,w,h = cv2.boundingRect(cntr)

    # crop to white region
    crop = deskewed[y:y+h, x:x+w]

    # alternately crop the input
    #crop = image[y:y+h, x:x+w]

    # save deskewed image
    cv2.imwrite("bw2_deskewed_{0}.png".format(i),crop)
    print("")
    i = i + 1

# save contour and rot rect images
cv2.imwrite("bw2_contours.png",contour_img)
cv2.imwrite("bw2_rotrects.png",rotrect_img)

# display result, though it won't show transparency
cv2.imshow("thresh", thresh)
cv2.imshow("clean", clean)
cv2.imshow("contours", contour_img)
cv2.imshow("rectangles", rotrect_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

轮廓图像: enter image description here 旋转矩形图像: enter image description here 前三个未旋转的图像: enter image description here enter image description here enter image description here 仿射变换旋转角度:
13.916877746582031
-42.87890625
18.8118896484375
-44.333797454833984
-38.65980911254883
-37.25965881347656
8.806793212890625
14.931419372558594
-37.405357360839844
-34.99202346801758
35.537681579589844
-35.350345611572266
-42.3245735168457
50.12316131591797
-42.969085693359375
52.750038146972656
45.0



2

你的代码正确地检测到了这些补丁,只有一个小错误在这里。

if cv2.contourArea(c) < 3000:
    continue

将3000降至100或以下的数值,因为您正在给出一个条件,即忽略低于3000的轮廓。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接