I'm trying to make a general object counting algorithm using python and openCV (open to try other methods) however I can't seem to get a good count on a variety of objects and don't know how to accomodate for that
https://imgur.com/a/yAkRxWH are some example test images.
This is for to speed up inventory counting of smaller objects.
**EDIT
This is my current code (simple blob detector)
# Standard imports
import cv2
import numpy as np;
# Read image
im = cv2.imread("./images/screw_simple.jpg", cv2.IMREAD_GRAYSCALE)
im = cv2.resize(im, (1440, 880))
# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 10 #10
params.maxThreshold = 200 #200
# Filter by Area.
params.filterByArea = True # True
params.minArea = 500 #1500
# Filter by Circularity
params.filterByCircularity = True #True
params.minCircularity = 0.1 #0.1
# Filter by Convexity
params.filterByConvexity = True #True
params.minConvexity = 0.0 #0.87
# Filter by Inertia
params.filterByInertia = True #True
params.minInertiaRatio = 0.0 #0.01
# 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)
# 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
total_count = 0
for i in keypoints:
total_count = total_count + 1
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)
print(total_count)
Here are the results I'm getting: https://imgur.com/a/id6OlIA
How can I improve this algorithm to get better detection for a general use case of objects without having to modify the parameters each time for each object?
You can try with an OpenCV approach, you could use a
SimpleBlobDetector
Obviously this is a test image and the result I got is also not perfect, since there are a lot of hyperparameters to set. The hyperparameters make it pretty flexible, so it is a decent place to start from.
This is what the Detector does (see details here):
Thresholding: Convert the source images to several binary images by thresholding the source image with thresholds starting at minThreshold. These thresholds are incremented by thresholdStep until maxThreshold. So the first threshold is minThreshold, the second is minThreshold + thresholdStep, the third is minThreshold + 2 x thresholdStep, and so on.
Grouping: In each binary image, connected white pixels are grouped together. Let’s call these binary blobs.
Merging: The centers of the binary blobs in the binary images are computed, and blobs located closer than minDistBetweenBlobs are merged.
Center & Radius Calculation: The centers and radii of the new merged blobs are computed and returned.
Find the code bellow the image.
# Standard imports
import cv2
import numpy as np
# Read image
im = cv2.imread("petri.png", cv2.IMREAD_COLOR)
# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 0
params.maxThreshold = 255
# Set edge gradient
params.thresholdStep = 5
# Filter by Area.
params.filterByArea = True
params.minArea = 10
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector_create(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)
For better readability I rather put this in a second answer: You could
use a segmentation approach e.g. watershed algorithm
Any grayscale image can be viewed as a topographic surface where high intensity denotes peaks and hills while low intensity denotes valleys. You start filling every isolated valleys (local minima) with different colored water (labels). As the water rises, depending on the peaks (gradients) nearby, water from different valleys, obviously with different colors will start to merge. To avoid that, you build barriers in the locations where water merges. You continue the work of filling water and building barriers until all the peaks are under water. Then the barriers you created gives you the segmentation result. This is the "philosophy" behind the watershed.
Related
I am playing a sports called "Stockschiessen" in Austria. Its similar to curling. Now I came to that thought of automating the detection of the winner. So I started coding something with opencv in python. It ends up in a big problem. I can detect Circles but the "Stocks" won't be directly under the camera so I have to detect ellipses or ovals. I tried a lot but found no solution. So I just need a solution to detect these and after that I am able to calculate the distances of each "Stock".
The last Code I tried was from Geeks for Geeks:
import numpy as np
# Load image
image = cv2.imread('C://gfg//images//blobs.jpg', 0)
# Set our filtering parameters
# Initialize parameter settiing using cv2.SimpleBlobDetector
params = cv2.SimpleBlobDetector_Params()
# Set Area filtering parameters
params.filterByArea = True
params.minArea = 100
# Set Circularity filtering parameters
params.filterByCircularity = True
params.minCircularity = 0.9
# Set Convexity filtering parameters
params.filterByConvexity = True
params.minConvexity = 0.2
# Set inertia filtering parameters
params.filterByInertia = True
params.minInertiaRatio = 0.01
# Create a detector with the parameters
detector = cv2.SimpleBlobDetector_create(params)
# Detect blobs
keypoints = detector.detect(image)
# Draw blobs on our image as red circles
blank = np.zeros((1, 1))
blobs = cv2.drawKeypoints(image, keypoints, blank, (0, 0, 255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
number_of_blobs = len(keypoints)
text = "Number of Circular Blobs: " + str(len(keypoints))
cv2.putText(blobs, text, (20, 550),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 100, 255), 2)
# Show blobs
cv2.imshow("Filtering Circular Blobs Only", blobs)
cv2.waitKey(0)
cv2.destroyAllWindows()
And this is an example image, one with the "Stocks" directly under the camera and the second one is a bit disturbed. It should find the ellipses on the second image.
Example 1
Example 2
Edit:
Here are these two pictures with grayscale and thresh. I think the circles can be seen well.
BlackAndWhite of Example1
BlackAndWhite of Example2
I am trying to write an algorithm to systematically determine how many different "curves" are in an image. Example Image. I'm specifically interested in the white lines here, so I've used a color threshold to mask the rest of the image and only get the white pixels. These lines represent a path run by a player (wide receivers in the NFL), so I'm interested in the x and y coordinates that the path represents - and each "curve" represents a different path that the player took (or "route"). All curves should start on or behind the blue line.
However, while I can get just the white pixels, I can't figure out how to systematically identify the separate curves. In this example image, there are 8 white curves (or routes) present. I've identified those curves in this image. I tried edge detection, and then using scipy ndimage to get the number of connected components, but because the curves overlap it counts them as connected and only gives me 3 labeled components for this image as opposed to eight. Here's what the edge detection output looks like. Is there a better way to go about this? Here is my sample code.
import cv2
from skimage.morphology import skeletonize
import numpy as np
from scipy import ndimage
#Read in image
image = cv2.imread('example_image.jpeg')
#Color boundary to get white pixels
lower_white = np.array([230, 230, 230])
upper_white = np.array([255, 255, 255])
#mask image for white pixels
mask = cv2.inRange(image, lower_white, upper_white)
c_pixels = cv2.bitwise_and(image, image, mask=mask)
#make pixels from 0 to 1 form to use in skeletonize
c_pixels = c_pixels.clip(0,1)
ske_c = skeletonize(c_pixels[:,:,1]).astype(np.uint8)
#Edge Detection
inputImage =ske_c*255
edges = cv2.Canny(inputImage,100,200,apertureSize = 7)
#Show edges
cv2.imshow('edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
#Find number of components
# smooth the image (to remove small objects); set the threshold
edgesf = ndimage.gaussian_filter(edges, 1)
T = 50 # set threshold by hand to avoid installing `mahotas` or
# `scipy.stsci.image` dependencies that have threshold() functions
# find connected components
labeled, nr_objects = ndimage.label(edgesf > T) # `dna[:,:,0]>T` for red-dot case
print("Number of objects is %d " % nr_objects)
I have some colored rectangles in my image that I successfully recognize by HSV thresholding. The result looks like this:
Now I want to detect the big blob as one point. I tried it with cv2.SimpleBlobDetector() and custom parameters:
import cv2
import numpy as np
mask = cv2.imread('mask.png')
original = cv2.imread('original.png')
params = cv2.SimpleBlobDetector_Params()
# thresholds
params.minThreshold = 10
params.maxThreshold = 200
#params.thresholdStep = 20
# filter by area
params.filterByArea = True
params.minArea = 1
params.maxArea = 10000
# filter by circularity
params.filterByCircularity = False
# filter by convexity
params.filterByConvexity = False
# filter by inertia
params.filterByInertia = False
detector = cv2.SimpleBlobDetector(params)
keypoints = detector.detect(mask)
img_keypoints = cv2.drawKeypoints(original, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imwrite('keypoints.png', img_keypoints)
This is how the result AND the original picture looks like:
I would expect a red point sitting in the center of the green point.
How can I fix this? Help is very appreciated.
EDIT: I forgot to mention: Is it really necessary to generate multiple binary images in cv2.SimpleBlobDetector() since I already have a binary image as input? Is it OK to change the values to the following:
params.minThreshold = 127
params.maxThreshold = 127
to reduce unnecessary CPU usage by generating binary images?
EDIT2 : Please note that I'm using OpenCV 2, not 3
Thank you.
When using cv2.SimpleBlobDetector(), it looks for blobs that are of a darker shade. In your case, the rectangle in mask is in white while the rest of the image is dark. As a result it is unable to find any blobs for the custom parameters set.
I just made a few changes to the existing code:
Read the mask as a grayscale image not a color image:
mask = cv2.imread('mask.png', 0)
Convert the mask to a binary image with the rectangle highlighted in dark:
ret, mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY_INV)
Proceeding from here using you code gave the following result as you expected.
Result:
I'm trying to identify brain tumors with blob detection in Open CV, but so far, Open CV only detects tiny circles in brain MRIs, but never the tumor itself.
Here's the code:
import cv2
from cv2 import SimpleBlobDetector_create, SimpleBlobDetector_Params
import numpy as np
# Read image
def blobber(filename):
im = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector_create()
params = cv2.SimpleBlobDetector_Params()
# Filter by Area.
params.filterByArea = True
params.minArea = 50
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.1
# 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)
# 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)
Here's what happens when I feed the program an image of a b/w contrasted brain (I contrasted the brain so the tumor would appear in black, and the rest of the brain would be mostly white):
The tumor is not a perfect circle, by any means, but it's clearly the biggest "blob" in the brain. Open CV can't pick it up, I suspect because it has a black outer shell, and a white core.
Only when I choose a more distinguishable tumor, without a large white inner core, can it pick up the tumor.
Any advice? I need to be able to peel these blobs (once they work accurately) out of the original pictures and use their keypoints to reconstruct the entire 3D tumor in the brain from JUST the 2D tumor in each slice. I'm a bit far removed from that step, but this blob detector issue is the crucial link between 2D and 3D. Appreciate all help!
probaply the largest blob is the skull itself.
if you distract the largest blob, and keep the next big thing use the contour as outsides
So I am trying to plan the approach I want to take to count vehicles and pedestrians in a video. Here are my basic steps for the approach I want to take.
Use background subtraction to distinguish between moving objects.
Use cv2.SimpleBlobDetector to detect blobs from the mask generated in the BGS step and return the keypoints.
Perform tracking of all blobs ( Not yet implemented in the example ) with the given keypoints.
The question: Can this approach be applied to both pedestrian and vehicles and if so, I am not clear on how can one distinguish the different blobs?
I am wondering if may be the size of the blob can be a used to distinguish between pedestrians ( small blobs ) and vehicles ( larger blobs ). However, I am not sure how to handle the case of a vehicle being further away from the source and hence appearing to be small.
import numpy as np
import cv2
cap = cv2.VideoCapture('video.avi')
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
fgbg = cv2.BackgroundSubtractorMOG(500, 6, 0.9, 1)
# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()
# Change thresholds
params.minThreshold = 10;
params.maxThreshold = 200;
# Filter by Area.
params.filterByArea = True
params.minArea = 400
# Filter by Circularity
params.filterByCircularity = True
params.minCircularity = 0.1
# Filter by Convexity
params.filterByConvexity = True
params.minConvexity = 0.87
# Filter by Inertia
params.filterByInertia = True
params.minInertiaRatio = 0.01
# 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)
while(1):
ret, frame = cap.read()
fgmask = fgbg.apply(frame)
fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
#fgmask = frame;
# Detect blobs.
keypoints = detector.detect(fgmask)
# 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(frame, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
print keypoints
cv2.imshow('frame',im_with_keypoints)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
cap.release()
cv2.destroyAllWindows()
I would suggest not to take blob area approach as a way to distinguish pedestrians from vehicles. An obvious drawback has been already explained by you - more distant cars would be for sure taken as a pedestrians.
There is a necessity to involve more complicated logic between steps 2 and 3 e.g.:
person detector based on HOG - see http://lear.inrialpes.fr/people/triggs/pubs/Dalal-cvpr05.pd. Already implemented in OpenCV, empirecally to have good accuracy in person detection.
car detector / car features detector (wheels, plate number etc) based on Haar classifier - need to prepare your own with the tools opencv provided. Having good classifier for detecting some car feature, you will increase accuracy of overall detection by increasing number of car detected positives.
Having at least one of those bullets in your final solution would be a must-have for me to have a good accuracy solution. Having them both will even have accuracy better.