Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

to detect patches in binary images using opencv python

I want to detect all the patches in the enter image description hereimage, I attached the code used to detect them:

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

I get output image as hereenter image description here

I want to detect all of them, can anyone tell me how to achieve this I new to image processing

like image 927
suji Avatar asked Jun 03 '20 06:06

suji


2 Answers

Here is how I would extract and rotate each blob in your image using Python OpenCV.

  • Read the input
  • Convert to gray
  • Threshold
  • Apply morphology open and close to clean small spots
  • Get all the external contours
  • Loop over each contour and do the following:
  • Draw the contour on a copy of the input image
  • Get the rotated rectangle of the contour and extract its center, dimensions and rotation angle
  • Get the corners of the rotated rectangle
  • Draw the rotated rectangle on another copy of the input
  • Correct the rotation angle for image unrotation
  • Generate a mask image with the filled rotated rectangle
  • Apply the mask image to the morphology cleaned image to remove near-by other white regions
  • Get the affine warp matrix using the center and corrected rotation angle
  • Unrotated the the masked image using warpAffine
  • Get the contour of the one blob in the unrotated image
  • Get the contours bounding box
  • Crop the masked image (or alternately crop the input image)
  • Save the cropped image
  • Exit the loop
  • Save the contour and rotrect images


Input:

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


Contour image:

enter image description here

Rotated rectangles images:

enter image description here

First 3 unrotated images:

enter image description here

enter image description here

enter image description here

Affine warp rotation angles:

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


like image 61
fmw42 Avatar answered Nov 05 '22 03:11

fmw42


your code is correct for detecting those patches, only a minor mistake is here

if cv2.contourArea(c) < 3000:
    continue

reduce 3000 to 100 or below values, because your are giving a condition as contours below 3000 to be neglect

like image 2
kiki Avatar answered Nov 05 '22 02:11

kiki