From 34c6d866ce0423bcbf3b796a526fe7d3a45a0dae Mon Sep 17 00:00:00 2001 From: Michael Sekatchev <64549194+msekatchev@users.noreply.github.com> Date: Tue, 2 Jun 2020 12:10:35 -0700 Subject: [PATCH 1/3] Added bolt and pmt-id labels on output mask - Added automatic bolt and pmt-id labels on output mask - Added a temporary PMT-ID input for labelling - Changed multiplier in `ret_centres`: `L=int(r*1.5)` (Image was being cropped otherwise) - Temporarily readded drawing detected bolt centres on mask to make sure they're being detected properly. Tested using masks with radius 120 for the PMT and 10 for the bolts. --- find_centroids.py | 123 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 108 insertions(+), 15 deletions(-) diff --git a/find_centroids.py b/find_centroids.py index ab65ded..38a6c9b 100644 --- a/find_centroids.py +++ b/find_centroids.py @@ -2,6 +2,8 @@ import numpy as np from skimage import color, img_as_ubyte import os, sys +import math +#find_centroids("B.png") def find_centroids(img_path) : @@ -17,10 +19,9 @@ def find_centroids(img_path) : pmts_gray = cv2.inRange(train_labels, lower_pmts, upper_pmts) bolts_gray = cv2.inRange(train_labels, lower_bolts, upper_bolts) img = cv2.cvtColor(bolts_gray,cv2.COLOR_GRAY2BGR) - cv2.imwrite("pmts_gray--4.png",pmts_gray) - cv2.imwrite("bolts_gray--4.png",bolts_gray) - cv2.imwrite("img--4.png",img) - + #cv2.imwrite("pmts_gray--4.png",pmts_gray) + #cv2.imwrite("bolts_gray--4.png",bolts_gray) + #cv2.imwrite("img--4.png",img) @@ -67,7 +68,7 @@ def ret_centres(image, params) : x = params[1] y = params[0] r = params[2] - L = int(r*1.1) + L = int(r*1.5) gray_image = image[x-L:x+L, y-L:y+L] ret,thresh = cv2.threshold(gray_image,127,255,0) gray_image = color.gray2rgb(img_as_ubyte(gray_image)) @@ -95,33 +96,125 @@ def ret_centres(image, params) : continue return out ################################################################ - + + + + + ################################################################ + # From a list of bolt positions and circle parameters, orders bolts in clockwise order starting from top over 360 degrees + + #returns a list of bolts in clockwise order + def order_bolts(circle, bolt_ring) : + + out = [] + + #circle parameters + circ_x = circle[0] + circ_y = circle[1] + circ_r = circle[2] + L = int(circ_r*1) + + #bolt locations found beforehand + nodes = np.asarray(bolt_ring) + + #loop over 2pi in (60 for now) steps finding closest bolts + for i in np.linspace(0, 2*3.14159, num=60) : + + #point on circle (scaled by radius of hough transform) to search near + search_x = int(circ_x + L*np.sin(i)) + search_y = int(circ_y - L*np.cos(i)) + + #find bolt closest to point + dist = np.sum((nodes - [search_y,search_x])**2, axis=1) + closest = np.argmin(dist) + + #only append each bolt coordinate once + if bolt_ring[closest] not in out : + out.append(bolt_ring[closest]) + + return out + + ################################################################ #find circles in image circles = find_pmts(pmts_gray) - bolt_centres = [] + #loop over circles and find bolts, end up with list of circles, each a list of bolts (no circle parameters are saved ) for i in circles[0, :] : bolt_ring = ret_centres(bolts_gray, i) - bolt_centres.append(bolt_ring) - + bolts_ord = order_bolts(i, bolt_ring) + pmtID = input("Input PMT number:\n-->") #Draw hough circles on img cv2.circle(img,(i[0],i[1]),i[2],(0,255,0),2) cv2.circle(img,(i[0],i[1]),2,(0,255,0),3) + #draw bolt locations and number them + bolt_no = 0 + #print("I is ",i) + cv2.putText(img, pmtID, (i[0]-47, i[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,255), 1) + ######################### Automatic bolt number labelling on mask ############################## + radius = i[2] + for j in bolts_ord : + bolt_no += 1 + + #Find the angle at the centre of the PMT between the x axis and + #the line pointing from the centre of the PMT to the centre of the bolt + lengthx = j[1]-i[0] + lengthy = j[0]-i[1] + length = math.sqrt(lengthx**2+lengthy**2) + angle = np.arctan(lengthy/lengthx) + + #Calculate text location based on this angle and the location of the bolt along the circle. + + #bolt 1 + if(bolt_no==1): + textx = int(j[1]) + texty = int(j[0]-(radius/3)) + #bolts 2 through 14 (on the right side of the circle/the 1st and 4th quadrant) + elif(bolt_no<=12): + textx = int(j[1]+(radius/3.4)*np.cos(angle)) + texty = int(j[0]+(radius/3.4)*np.sin(angle)) + #bolt 13 + elif(bolt_no==13): + textx = int(j[1]+(radius/3.4)*np.cos(angle)) + texty = int(j[0]+abs((radius/3.4)*np.sin(angle))) + #bolts 14 through 19 (on the 3rd quadrant) + elif(bolt_no<=19): + textx = int(j[1]-(radius/2.4)*np.cos(angle)) + texty = int(j[0]-(radius/2.4)*np.sin(angle)) + #bolts 20 through 24 (on the 2nd quadrant) + else: + textx = int(j[1]-(radius/2.6)*np.cos(angle)) + texty = int(j[0]-(radius/2.6)*np.sin(angle)) + pointerx = int((textx+textx+20)/2) + pointery = int((texty+texty-20)/2) + cv2.line(img, (j[1],j[0]), (pointerx,pointery), (0,177,177), thickness=1, lineType=8, shift=0) + cv2.rectangle(img,(textx,texty),(textx+20,texty-20),(0,0,0), thickness=-1, lineType=8, shift=0) + + cv2.putText(img, f'{bolt_no}', (textx, texty), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255,0,255), 1) + + ######################### Automatic bolt number labelling on mask ############################## + #draw bolt locations - for i in bolt_centres : - for j in i : - cv2.circle(img, (j[1], j[0]), 5, (0,0,255), -1) - print(bolt_centres) + for i in bolts_ord : + print(i[0],i[1]) + #for j in i : + cv2.circle(img, (i[1], i[0]), 5, (0,0,255), -1) + #print(j) + print(circles) print("SAVING") - cv2.imwrite('out.png', img ) - + + base = os.path.basename(img_path) + filename = os.path.splitext(base)[0] + outputMaskName = os.path.join(filename+'-labelled.png') + print("Saving labelled output mask to: ",outputMaskName) + #save output mask in code directory + cv2.imwrite(outputMaskName, img) From b084038fe4867cca74741ea14a90ae243fb4f8ca Mon Sep 17 00:00:00 2001 From: Michael Sekatchev <64549194+msekatchev@users.noreply.github.com> Date: Tue, 2 Jun 2020 12:12:34 -0700 Subject: [PATCH 2/3] Two demo images for testing the code Created two simple PMT masks using radius 120 for the PMT and 10 for the bolts. I used these to test my code. --- DemoImages.zip | Bin 0 -> 28895 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 DemoImages.zip diff --git a/DemoImages.zip b/DemoImages.zip new file mode 100644 index 0000000000000000000000000000000000000000..97b541e870297ff61f2ad51d947422193d9dfca5 GIT binary patch literal 28895 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 literal 0 HcmV?d00001 From 584086dae1703bbee9ef582e3eacc2144fdccbee Mon Sep 17 00:00:00 2001 From: Michael Sekatchev <64549194+msekatchev@users.noreply.github.com> Date: Wed, 3 Jun 2020 10:38:02 -0700 Subject: [PATCH 3/3] Update find_centroids.py --- find_centroids.py | 51 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/find_centroids.py b/find_centroids.py index 38a6c9b..9e3c825 100644 --- a/find_centroids.py +++ b/find_centroids.py @@ -27,10 +27,6 @@ def find_centroids(img_path) : - - - #print('images made') - #Calls HoughCircles() to find multiple PMTs in an image #Outpts list of circle parameters @@ -45,9 +41,9 @@ def find_pmts(gray_img) : minRadius=100, maxRadius=200 ) - #print(circles) - ##Error over here vvvv, because circles is empty. + + circles = np.uint16(np.around(circles)) img2 = cv2.cvtColor(edges,cv2.COLOR_GRAY2BGR) @@ -90,7 +86,6 @@ def ret_centres(image, params) : cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) out.append([cY + (x-L), cX + (y-L)]) - # print(area) else: continue @@ -156,6 +151,7 @@ def order_bolts(circle, bolt_ring) : bolt_no = 0 #print("I is ",i) cv2.putText(img, pmtID, (i[0]-47, i[1]-10), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,255), 1) + ######################### Automatic bolt number labelling on mask ############################## radius = i[2] for j in bolts_ord : @@ -166,48 +162,51 @@ def order_bolts(circle, bolt_ring) : lengthx = j[1]-i[0] lengthy = j[0]-i[1] length = math.sqrt(lengthx**2+lengthy**2) - angle = np.arctan(lengthy/lengthx) + if(lengthx==0): + angle=3.1415926/2 + else: + angle = np.arctan(lengthy/lengthx) #Calculate text location based on this angle and the location of the bolt along the circle. #bolt 1 if(bolt_no==1): textx = int(j[1]) - texty = int(j[0]-(radius/3)) + texty = int(j[0]-(32)) #bolts 2 through 14 (on the right side of the circle/the 1st and 4th quadrant) elif(bolt_no<=12): - textx = int(j[1]+(radius/3.4)*np.cos(angle)) - texty = int(j[0]+(radius/3.4)*np.sin(angle)) + textx = int(j[1]+(28)*np.cos(angle)) + texty = int(j[0]+(28)*np.sin(angle)) #bolt 13 elif(bolt_no==13): - textx = int(j[1]+(radius/3.4)*np.cos(angle)) - texty = int(j[0]+abs((radius/3.4)*np.sin(angle))) + textx = int(j[1]+abs((34)*np.cos(angle))) + texty = int(j[0]+abs((34)*np.sin(angle))) #bolts 14 through 19 (on the 3rd quadrant) elif(bolt_no<=19): - textx = int(j[1]-(radius/2.4)*np.cos(angle)) - texty = int(j[0]-(radius/2.4)*np.sin(angle)) + textx = int(j[1]-(40)*np.cos(angle)) + texty = int(j[0]-(40)*np.sin(angle)) #bolts 20 through 24 (on the 2nd quadrant) else: - textx = int(j[1]-(radius/2.6)*np.cos(angle)) - texty = int(j[0]-(radius/2.6)*np.sin(angle)) + textx = int(j[1]-(37)*np.cos(angle)) + texty = int(j[0]-(37)*np.sin(angle)) pointerx = int((textx+textx+20)/2) pointery = int((texty+texty-20)/2) cv2.line(img, (j[1],j[0]), (pointerx,pointery), (0,177,177), thickness=1, lineType=8, shift=0) - cv2.rectangle(img,(textx,texty),(textx+20,texty-20),(0,0,0), thickness=-1, lineType=8, shift=0) - - cv2.putText(img, f'{bolt_no}', (textx, texty), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255,0,255), 1) - + + cv2.rectangle(img,(textx,texty),(textx+15,texty-15),(0,0,0), thickness=-1, lineType=8, shift=0) + #cv2.circle(img,(textx,texty),10,(0,0,0),-1) + cv2.circle(img, (j[1], j[0]), 5, (0,0,255), -1) + cv2.putText(img, f'{bolt_no}', (textx, texty), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,0,255), 1) ######################### Automatic bolt number labelling on mask ############################## #draw bolt locations - for i in bolts_ord : - print(i[0],i[1]) - #for j in i : - cv2.circle(img, (i[1], i[0]), 5, (0,0,255), -1) - #print(j) + #for i in bolts_ord : + print(j[0],j[1]) + + print(circles) print("SAVING")