python - How to find the centre of these sometimes-overlapping circles -
as part of project i'm working on, need find centre-point of "blobs" in image using opencv python. i'm having bit of trouble it, , appreciate or insight :)
my current method to: contours of images, overlay ellipses on those, use blob detector find centre of each of these. works well, have extraneous blobs need ignore, , blobs touching each-other.
here's example of when goes well: good source image: after extracting contours: with blobs detected:
and when goes poorly (you can see it's incorrectly overlayed ellipse on 3 blobs, , detected 1 don't want): bad source image: after extracting contours: with blobs detected:
this code use. i'm unsure of other option.
def process_and_detect(img_path): img = cv2.imread(path) imgray = cv2.cvtcolor(img, cv2.color_bgr2gray) ret, thresh = cv2.threshold(imgray, 50, 150, 0) im2, contours, hierarchy = cv2.findcontours(thresh, cv2.retr_tree, cv2.chain_approx_simple) drawn_img = np.zeros(img.shape, np.uint8) min_area = 50 min_ellipses = [] cnt in contours: if cv2.contourarea(cnt) >= min_area: ellipse = cv2.fitellipse(cnt) cv2.ellipse(drawn_img,ellipse,(0,255,0),-1) plot_img(drawn_img, size=12) # change thresholds params = cv2.simpleblobdetector_params() params.filterbycolor = true params.blobcolor = 255 params.filterbycircularity = true params.mincircularity = 0.75 params.filterbyarea = true params.minarea = 150 # set detector detector = cv2.simpleblobdetector_create(params) # detect blobs. keypoints = detector.detect(drawn_img) k in keypoints: x = round(k.pt[0]) y = round(k.pt[1]) line_length = 20 cv2.line(img, (x-line_length, y), (x+line_length, y), (255, 0, 0), 2) cv2.line(img, (x, y-line_length), (x, y+line_length), (255, 0, 0), 2) plot_img(img, size=12)
thank reading far, sincerely hope can me out, or point me in right direction. thanks!
blob detector
currently, implementation redundant. simpleblobdetector()
docs:
the class implements simple algorithm extracting blobs image:
- convert source image binary images applying thresholding several thresholds minthreshold (inclusive) maxthreshold (exclusive) distance thresholdstep between neighboring thresholds.
- extract connected components every binary image findcontours() , calculate centers.
- group centers several binary images coordinates. close centers form 1 group corresponds 1 blob, controlled mindistbetweenblobs parameter.
- from groups, estimate final centers of blobs , radiuses , return locations , sizes of keypoints.
so you're implementing part of steps already, might give unexpected behavior. try playing parameters see if can figure out work (try creating trackbars play parameters , live results of algorithm different blob detector parameters).
modifying pipeline
however, you've got of own pipeline written, can remove blob detector , implement own algorithm. if drop threshold bit, can marked circles, , blob detection simple contour detection. if have separate contour each blob, can calculate centroid of contour moments()
. example:
def process_and_detect(img_path): img = cv2.imread(img_path) imgray = cv2.cvtcolor(img, cv2.color_bgr2gray) ret, thresh = cv2.threshold(imgray, 100, 255, cv2.thresh_binary) contours = cv2.findcontours(thresh, cv2.retr_tree, cv2.chain_approx_simple)[1] line_length = 20 c in contours: if cv2.contourarea(c) >= min_area: m = cv2.moments(c) x = int(m['m10']/m['m00']) y = int(m['m01']/m['m00']) cv2.line(img, (x-line_length, y), (x+line_length, y), (255, 0, 0), 2) cv2.line(img, (x, y-line_length), (x, y+line_length), (255, 0, 0), 2)
getting more involved
this same pipeline can used automatically loop through threshold values don't have guess , hardcode values. since blobs seem same size, can loop through until contours have same area. e.g. finding median contour size, defining percentage of median size above , below you'll allow, , checking if contours detected fit in bounds.
here's animated gif
of mean. notice gif
stops once contours separated:
then can find centroids of separated contours. here's code:
def process_and_detect(img_path): img = cv2.imread(img_path) imgray = cv2.cvtcolor(img, cv2.color_bgr2gray) thresh_val in range(0, 255): # threshold , detect contours thresh = cv2.threshold(imgray, thresh_val, 255, cv2.thresh_binary)[1] contours = cv2.findcontours(thresh, cv2.retr_tree, cv2.chain_approx_simple)[1] # filter contours area min_area = 50 filtered_contours = [c c in contours if cv2.contourarea(c) >= min_area] area_contours = [cv2.contourarea(c) c in filtered_contours] # acceptable deviation median contour area median_area = np.median(area_contours) dev = 0.3 lowerb = median_area - dev*median_area upperb = median_area + dev*median_area # break when contours within deviation median area if ((area_contours > lowerb) & (area_contours < upperb)).all(): break # draw center location of blobs line_length = 8 cross_color = (255, 0, 0) c in filtered_contours: m = cv2.moments(c) x = int(m['m10']/m['m00']) y = int(m['m01']/m['m00']) cv2.line(img, (x-line_length, y), (x+line_length, y), cross_color, 2) cv2.line(img, (x, y-line_length), (x, y+line_length), cross_color, 2)
note here looped through possible threshold values range(0, 255)
give 0, 1, ..., 254
start higher , skip through few values @ time with, say, range(50, 200, 5)
50, 55, ..., 195
of course faster.
Comments
Post a Comment