问题
I'm doing an at-home project basically just for fun but I'm having more trouble than anticipated. I want to be able to find the pitch mark in this sample image. enter image description here
I've followed some tutorials and things to load the image from a location and run simple blob detection on the image. My code currently is as follows ->
import cv2
import numpy as np
# Read in the image in grayscale
img = cv2.imread('/home/pi/Downloads/divot1.jpeg', cv2.IMREAD_GRAYSCALE)
params = cv2.SimpleBlobDetector_Params()
params.filterByArea = True
params.minArea = 50
# Determine which openCV version were using
if cv2.__version__.startswith('2.'):
detector = cv2.SimpleBlobDetector(params)
else:
detector = cv2.SimpleBlobDetector_create(params)
# Detect the blobs in the image
keypoints = detector.detect(img)
print(len(keypoints))
# Draw detected keypoints as red circles
imgKeyPoints = cv2.drawKeypoints(img, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Display found keypoints
cv2.imshow("Keypoints", imgKeyPoints)
cv2.waitKey(0)
cv2.destroyAllWindows()
I understand that there are A LOT more image manipulation techniques when using openCV but I was hoping someone could point me in the right direction. I've tried some things like various blur types to kind of get rid of some of the 'noise' of each single blade of grass. I also tried messing with a few parameters although I'm not sure the ones I was using were helping (min/maxAREA, color, convexity)
The end goal is to be able to find that dark brown abnormality in the image, and return the center "coordinate" of that shape.
回答1:
Nobody mentioned blurring yet so I will.
The fine detail of the grass is equivalent to noise that will bother you in whatever you do with the picture as is. Blur it.
Edit: since I was asked for some parameter-"less" approaches:
Saliency.
Or good old statistics.
im = cv.imread("green-blurred.jpg")
yuv = cv.cvtColor(im, cv.COLOR_BGR2YCrCb) # or COLOR_BGR2YUV, should work equivalently here
#uv = yuv[:,:,(2,3)]
mean = np.mean(yuv, axis=(0,1))
std = np.std(yuv, axis=(0,1))
#mask = ((yuv - mean) / std >= 4.5).any(axis=2)
# EDIT: there needs to be an abs() on that difference, we want magnitudes, no signs
mask = (np.abs(yuv - mean) / std >= 4.5).any(axis=2)
mask_u8 = mask.astype(np.uint8) * 255
#grayscale = (np.abs(yuv - mean) / std / 5).max(axis=2)
cv.imshow("mask", mask_u8)
cv.waitKey(-1)
not a perfect approach but can probably be adjusted.
Use whatever you like to localize the blob. findContours
would be an easy choice. Use cv::moments to get some statistics you can turn into a centroid (center of mass) for the blob.
回答2:
Sorry, I don't have time to implement it, but for this particular case, something like this should work:
- Google for "color picker" and find the correct HSV range of colors. For this brown, it looks like you want a range of from about 20 degrees to about 40 degrees.
- Convert your image to the HSV color space.
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
- Use cv2.inRange to create a mask of where the brown spots are. mask =
cv2.inRange(hsv_img, (20,0,0), (40,255,255))
- Convert your mask to a black and white image.
img2 = np.where(mask, np.uint8(255), np.uint8(0))
- Use cv2.findContours to find the brown spots.
contours,_ = cv2.findContours(img2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
- Get the largest spot.
brown_spot = sorted(contours, key=cv2.contourArea, reverse=True)[0]
- Draw the contour.
cv2.drawContours(img, [brown_spot], -1, (255,0,0), 3)
There might be some things I missed or some mistakes with this, but I think you can play around with it and take it from here :)
edit I overlooked the part where you also want the center coordinate. If you want the center of mass of the brown spot, get the average x coordinate of all of the x coordinates and the average y coordinate of all of the y coordinates. That should be your center of mass coordinate. If I remember correctly the contour has an extra dimension on it, which you may need to remove with np.squeeze. After doing that you can get the average of the y coordinates (the first column) with np.mean(brown_spot[:,0]) and the average of the x coordinates (the second column) with np.mean(brown_spot[:,1]). cv2.findContours only finds perimeters and the spacing is not consistent, so your center of mass might be a little off. If you want a more accurate center of mass (or to also account for empty areas within the brown spot), you will have to use connectedComponents instead of findContours.
来源:https://stackoverflow.com/questions/65138694/opencv-blob-defect-anomaly-detection