is there a way to threshold only if the blob size is more than (height,width)?
Example
import cv2
img_1 = cv2.imread('my_image_1.jpg')
thresh = cv2.threshold(img_1, 200, 255, cv2.THRESH_BINARY)[1]
For the purposes of thresholding, I want to ignore all pixels that are not inside a blob of say, 6 x 6 pixels that also meet the thresholding condition.
What is the best way to do this?
Please check this tutorial.
You can implement this by adding the following lines to your code,
params = cv2.SimpleBlobDetector_Params()
params.filterByArea = True
params.minArea = 20 #define minimum area
ver = (cv2.__version__).split('.')
if int(ver[0]) < 3 :
detector = cv2.SimpleBlobDetector(params)
else :
detector = cv2.SimpleBlobDetector_create(params)
keypoints = detector.detect(thresh)
im_with_keypoints = cv2.drawKeypoints(thresh, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
Related
I want to tag on the original image with the blobs that are found. But whenever I do the blob detection it only will make a new image like this:
Outcome image after blob:
However, I want to show the original image with the red tagging on it. Original image:
I just use the regular code for the blob detection. Is there another way to do the red circles on the original image? So its clear where they are.
im_gray = cv2.imread(img,cv2.IMREAD_GRAYSCALE)
(thresh, im_bw) = cv2.threshold(im_gray, 128, 255,cv2.THRESH_BINARY |
cv2.THRESH_OTSU)
thresh = 50
im_bw = cv2.threshold(im_gray, thresh, 255, cv2.THRESH_BINARY)[1]
#detect blobs based on features
params = cv2.SimpleBlobDetector_Params()
# Filter by Area.
params.filterByArea = True
params.minArea = 70
params.maxArea = 150
# Filter by Color (black=0)
params.filterByColor = False # Set true for cast_iron as we'll be detecting black regions
params.blobColor = 0
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.5
params.maxCircularity = 1
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.5
params.maxConvexity = 1
# Filter by InertiaRatio
params.filterByInertia = True
params.minInertiaRatio = 0.3
params.maxInertiaRatio = 0.9
# Distance Between Blobs
params.minDistBetweenBlobs = 0
#thresholded to value 70 detecting blobs:
detector = cv2.SimpleBlobDetector_create(params)
keypoints = detector.detect(im_bw)
print("Number of blobs detected are : ", len(keypoints))
#detect blobs: missing the detection based on features
im_with_keypoints = cv2.drawKeypoints(im_bw, keypoints, numpy.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
Your problem is your last line:
im_with_keypoints = cv2.drawKeypoints(im_bw, keypoints, numpy.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
Here you draw your key points on im_bw, which is not your original image but your thresholded image. If you replace im_bw here with your original image (e.g. use your grayscale version you already loaded as im_gray) you should get your desired result.
I'm trying to use OpenCV's SimpleBlobDetector. I'm using a very siple code, I read the image, I create the detector and I apply it. But when I excecute it I get the error Python has stopped working.
The image is properly read
Is a 1536x2048 image (too big?)
Could the PGN format be a problem?
import cv2
import numpy as np;
# Read image
im = cv2.imread("vlcsnap-2020-02-05-10h54m17s200.png", cv2.IMREAD_GRAYSCALE)
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector()
# Detect blobs.
keypoints = detector.detect(im) #No error if I coment this line
Does anyone have a clue of why would't this work?
Here is an answer that I posted some time ago in Python/OpenCV for doing blob analysis. Perhaps this will help you.
Input:
import numpy as np
import cv2
import math
# read image
img = cv2.imread("particles.jpg")
# convert to grayscale
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# apply Gaussian Blur
smoothed = cv2.GaussianBlur(gray, (0,0), sigmaX=9, sigmaY=9, borderType = cv2.BORDER_DEFAULT)
# do adaptive threshold on gray image
thresh = cv2.adaptiveThreshold(smoothed, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 65, 10)
cv2.imshow("Threshold", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Set up the SimpleBlobdetector with default parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 0
params.maxThreshold = 256
# Filter by Area.
params.filterByArea = True
params.minArea = 30
params.maxArea = 10000
# Filter by Color (black=0)
params.filterByColor = True
params.blobColor = 0
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.5
params.maxCircularity = 1
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.5
params.maxConvexity = 1
# Filter by InertiaRatio
params.filterByInertia = True
params.minInertiaRatio = 0
params.maxInertiaRatio = 1
# Distance Between Blobs
params.minDistBetweenBlobs = 0
# Do detecting
detector = cv2.SimpleBlobDetector_create(params)
# Get keypoints
keypoints = detector.detect(thresh)
print(len(keypoints))
print('')
# Get keypoint locations and radius
for keypoint in keypoints:
x = int(keypoint.pt[0])
y = int(keypoint.pt[1])
s = keypoint.size
r = int(math.floor(s/2))
print (x,y,r)
#cv2.circle(img, (x, y), r, (0, 0, 255), 2)
# Draw blobs
blobs = cv2.drawKeypoints(thresh, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imshow("Keypoints", blobs)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Save result
cv2.imwrite("particle_blobs.jpg", blobs)
I'm trying to teach my test automation framework to detect a selected item in an app using opencv (the framework grabs frames/screenshots from the device under test). Selected items are always a certain size and always have blue border which helps but they contain different thumbnail images. See the example image provided.
I have done a lot of Googling and reading on the topic and I'm close to getting it to work expect for one scenario which is image C in the example image. example image This is where there is a play symbol on the selected item.
My theory is that OpenCV gets confused in this case because the play symbol is basically circle with a triangle in it and I'm asking it to find a rectangular shape.
I found this to be very helpful: https://www.learnopencv.com/blob-detection-using-opencv-python-c/
My code looks like this:
import cv2
import numpy as np
img = "testimg.png"
values = {"min threshold": {"large": 10, "small": 1},
"max threshold": {"large": 200, "small": 800},
"min area": {"large": 75000, "small": 100},
"max area": {"large": 80000, "small": 1000},
"min circularity": {"large": 0.7, "small": 0.60},
"max circularity": {"large": 0.82, "small": 63},
"min convexity": {"large": 0.87, "small": 0.87},
"min inertia ratio": {"large": 0.01, "small": 0.01}}
size = "large"
# Read image
im = cv2.imread(img, cv2.IMREAD_GRAYSCALE)
# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = values["min threshold"][size]
params.maxThreshold = values["max threshold"][size]
# Filter by Area.
params.filterByArea = True
params.minArea = values["min area"][size]
params.maxArea = values["max area"][size]
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = values["min circularity"][size]
params.maxCircularity = values["max circularity"][size]
# Filter by Convexity
params.filterByConvexity = False
params.minConvexity = values["min convexity"][size]
# Filter by Inertia
params.filterByInertia = False
params.minInertiaRatio = values["min inertia ratio"][size]
# Create a detector with the parameters
detector = cv2.SimpleBlobDetector(params)
# Detect blobs.
keypoints = detector.detect(im)
for k in keypoints:
print k.pt
print k.size
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures
# the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show blobs
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
How do I get OpenCV to only look at the outer shape defined by the blue border and ignore the inner shapes (the play symbol and of course the thumbnail image)? I'm sure it must be do-able somehow.
there are many different techniques, that will do the job. I am not really sure how BlobDetector works, so I took anoter approach. Also I am not really sure what you need, but you can modify this solution for your needs.
import cv2
import numpy as np
from matplotlib.pyplot import figure
import matplotlib.pyplot as plt
img_name = "CbclA.png" #Image you have provided
min_color = 150 #Color you are interested in (from green channel)
max_color = 170
min_size = 4000 #Size of border you are interested in (number of pixels)
max_size = 30000
img_rgb = cv2.imread(img_name)
img = img_rgb[:,:,1] #Extract green channel
img_filtered = np.bitwise_and(img>min_color, img < max_color) #Get only colors of your border
nlabels, labels, stats, centroids = cv2.connectedComponentsWithStats(img_filtered.astype(np.uint8))
good_area_index = np.where(np.logical_and(stats[:,4] > min_size,stats[:,4] < max_size)) #Filter only areas we are interested in
for area in stats[good_area_index] : #Draw it
cv2.rectangle(img_rgb, (area[0],area[1]), (area[0] + area[2],area[1] + area[3]), (0,0,255), 2)
cv2.imwrite('result.png',img_rgb)
Take a look at documentation of connectedComponentsWithStats
Note: I am using Python 3
Edit: result image added
If I got it right, you want a rectangle bounding the blue box with curved edges. If this is the case, it's very easy.
Apply this -
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edged = cv2.Canny(gray, 75, 200) # You'll have to tune these
# Find contours
(_, contour, _) = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# This should return only one contour in 'contour' in your case
This should do but if you still get a contour (the bounding box) with curved edges apply this -
rect = cv2.approxPolyDP(contour, 0.02 * cv2.arcLength(contour, True), True)
# Play with the second parameter, appropriate range would be from 1% to 5%
I toyed around with this a bit more after reading your suggestions and found that blob detection is not the way to go. Using color recognition to find the contours solved the issue however as was suggested above. Thanks again!
My solution looks like this:
frame = cv2.imread("image.png")
color = ((200, 145, 0), (255, 200, 50))
lower_color = numpy.array(color[0], dtype="uint8")
upper_color = numpy.array(color[1], dtype="uint8")
# Look for the color in the frame and identify contours
color = cv2.GaussianBlur(cv2.inRange(frame, lower_color, upper_color), (3, 3), 0)
contours, _ = cv2.findContours(color.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
for c in contours:
rectangle = numpy.int32(cv2.cv.BoxPoints(cv2.minAreaRect(c)))
# Draw a rectangular frame around the detected object
cv2.drawContours(frame, [rectangle], -1, (0, 0, 255), 4)
cv2.imshow("frame", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
I got the program of blob detection in the following website: https://www.learnopencv.com/blob-detection-using-opencv-python-c/
It is quiet useful,but i found nothing change in the result after i change the parameters value.
like: even i set the parameter of color to 255(it is used to detect lighter blobs), the dark blobs still can be detected. Also, after i change the value of minimum area, the smallest blob also can be detected.
it seems nothing changes,the result is always like the following one:
the result of blob detection
Here is the code:
import cv2
import numpy as np;
# Read image
im = cv2.imread("blob.jpg", cv2.IMREAD_GRAYSCALE)
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector()
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 10; # the graylevel of images
params.maxThreshold = 200;
params.filterByColor = True
params.blobColor = 255
# Filter by Area.
params.filterByArea = False
params.minArea = 10000
# Detect blobs.
keypoints = detector.detect(im)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
Can anyone help me ? Thanks very much!!!
You change parameters but they are not used by the detector. Set them and then set up the detector:
import cv2
import numpy as np
# Read image
im = cv2.imread('blob.jpg', cv2.IMREAD_GRAYSCALE)
# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 10 # the graylevel of images
params.maxThreshold = 200
# Filter by Area.
params.filterByArea = True
params.minArea = 1500
# Create a detector with the parameters
detector = cv2.SimpleBlobDetector(params)
# Detect blobs.
keypoints = detector.detect(im)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(
im, keypoints, np.array([]), (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
cv2.imshow('Keypoints', im_with_keypoints)
cv2.waitKey(0)
Note: you have to use True for params.filterByArea if you want this parameter to be used and to not end on a segfault.
I have tried this code.
import sys
import numpy as np
sys.path.append('/usr/local/lib/python2.7/site-packages')
import cv2
from cv2.cv import *
img=cv2.imread("test2.jpg",cv2.IMREAD_COLOR)
gimg = cv2.imread("test2.jpg",cv2.IMREAD_GRAYSCALE)
b,g,r = cv2.split(img)
ret,thresh1 = cv2.threshold(gimg,127,255,cv2.THRESH_BINARY);
numrows = len(thresh1)
numcols = len(thresh1[0])
thresh = 170
im_bw = cv2.threshold(gimg, thresh, 255, cv2.THRESH_BINARY)[1]
trig=0
trigmax=0;
xmax=0
ymax=0
for x in range(0,numrows):
for y in range(0,numcols):
if(im_bw[x][y]==1):
trig=trig+1;
if(trig>5):
xmax=x;
ymax=y;
break;
print x,y,numrows,numcols,trig
roi=gimg[xmax:xmax+200,ymax-500:ymax]
cv2.imshow("roi",roi)
WaitKey(0)
here test2.jpg is what I am tring to do is to concentrate on the high intensity part of the image(i.e the circle with high intensity in image).But my code does not seem to do so.
Can anyone help?
I found answer to my question from here
here is my code
# import the necessary packages
import numpy as np
import cv2
# load the image and convert it to grayscale
image = cv2.imread('test2.jpg')
orig = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# apply a Gaussian blur to the image then find the brightest
# region
gray = cv2.GaussianBlur(gray, (41, 41), 0)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
image = orig.copy()
roi=image[maxLoc[1]-250:maxLoc[1]+250,maxLoc[0]-250:maxLoc[0]+250,2:2]
cv2.imshow("Robust", roi)
cv2.waitKey(0)
test2.jpg
ROI
Try checking whether a pixel is not zero - it turns out those pixels have a value of 255 after thresholding, as it is a grayscale image after all.
The threshold seems to be wrong also, but I don't really know what you want to see (display it with imshow - it isn't just the circle). And your code matches the number '3' in the bottom left corner, therefore the ROI matrix indices are invalid in your example.
EDIT:
After playing around with the image, I ended up using a different approach. I used the SimpleBlobDetector and did an erosion on the image before, so the region you're interested in remains connected. For the blob detector the program inverts the image first. (You may want to read a SimpleBlobDetector tutorial as I did, parts of the code are based on that page - many thanks to the author!)
The following code displays the procedure step by step:
import cv2
import numpy as np
# Read image
gimg = cv2.imread("test2.jpg", cv2.IMREAD_GRAYSCALE)
# Invert the image
im_inv = 255 - gimg
cv2.imshow("Step 1 - inverted image", im_inv)
cv2.waitKey(0)
# display at a threshold level of 50
thresh = 45
im_bw = cv2.threshold(im_inv, thresh, 255, cv2.THRESH_BINARY)[1]
cv2.imshow("Step 2 - bw threshold", im_bw)
cv2.waitKey(0)
# erosion
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(10,10))
im_bw = cv2.erode(im_bw, kernel, iterations = 1)
cv2.imshow('Step 3 - erosion connects disconnected parts', im_bw)
cv2.waitKey(0)
# Set up the detector with default parameters.
params = cv2.SimpleBlobDetector_Params()
params.filterByInertia = False
params.filterByConvexity = False
params.filterByCircularity = False
params.filterByColor = False
params.minThreshold = 0
params.maxThreshold = 50
params.filterByArea = True
params.minArea = 1000 # you may check with 10 --> finds number '3' also
params.maxArea = 100000 #im_bw.shape[0] * im_bw.shape[1] # max limit: image size
# Create a detector with the parameters
ver = (cv2.__version__).split('.')
if int(ver[0]) < 3 :
detector = cv2.SimpleBlobDetector(params)
else :
detector = cv2.SimpleBlobDetector_create(params)
# Detect blobs.
keypoints = detector.detect(im_bw)
print "Found", len(keypoints), "blobs:"
for kpt in keypoints:
print "(%.1f, %.1f) diameter: %.1f" % (kpt.pt[0], kpt.pt[1], kpt.size)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the
# circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(gimg, keypoints, np.array([]), (0,0,255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
This algorithm finds the coordinate (454, 377) as the center of the blob, but if you reduce the minArea to e.g. 10 then it will find the number 3 in the bottom corner as well.