from math import dist
import numpy as np
import cv2
from google.colab import drive
from google.colab.patches import cv2_imshow
cv2.destroyAllWindows()
# Next step is make sure measured rocks are touching rings
# distance between ((cCircle to cRock) - rRock) should be less than rCircle
# rRings = 72"; rRock = 5.7" (different for rinks and even rocks)
# rRockInPixels = rRingsPixels/72.0 * 5.7
# Accessing My Google Drive
drive.mount('/content/drive')
path = "/content/drive/My Drive/OpenCV/CurlingRingsFilled06.jpg" # Add the path of the image here
rockRatio = 5.70/72.0 # ratio of 12' ring radius vs rock radius in inches
# Read in colored image
image = cv2.imread(path, cv2.IMREAD_COLOR) # reads in as BGR
imgCircles= image.copy()
def rockInRings(rockDistance, ringRadius):
print('Rock Distance', rockDistance, ' Ring Radius:', ringRadius)
rockEdge = rockDistance - (ringRadius*rockRatio)
print('Edge of Rock:', rockEdge)
if int(rockEdge) <= ringRadius:
return True
else:
return False
def measureDistance(x1, y1, cx, cy):
pt1 = (x1,y1)
pt2 = (cx,cy)
return dist(pt1,pt2)
# Courtesy https://stackoverflow.com/a/59890380/2419128
def colorOfRock(image):
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# Red rocks
# Range for lower red
red_lower = np.array([0,120,70])
red_upper = np.array([10,255,255])
mask_red1 = cv2.inRange(hsv, red_lower, red_upper)
# Range for upper range
red_lower = np.array([170,120,70])
red_upper = np.array([180,255,255])
mask_red2 = cv2.inRange(hsv, red_lower, red_upper)
mask_red = mask_red1 + mask_red2
red_output = cv2.bitwise_and(image, image, mask=mask_red)
red_ratio=(cv2.countNonZero(mask_red))/(image.size/3)
red_ratio = np.round(red_ratio*100, 0)
#print("Red in image", red_ratio)
if red_ratio < 20: red_ratio = 0
# Yellow rocks
# Range for upper range
yellow_lower = np.array([20, 100, 100])
yellow_upper = np.array([30, 255, 255])
mask_yellow = cv2.inRange(hsv, yellow_lower, yellow_upper)
yellow_output = cv2.bitwise_and(image, image, mask=mask_yellow)
yellow_ratio = (cv2.countNonZero(mask_yellow))/(image.size/3)
yellow_ratio = np.round(yellow_ratio*100, 0)
#print("Yellow in image", yellow_ratio)
if int(yellow_ratio) < 20: yellow_ratio = 0
if (int(yellow_ratio) == int(red_ratio) == 0): return None
elif (red_ratio > yellow_ratio): return 'red'
else: return 'yellow'
def getRGB(img, x_center, y_center, radius):
circle_img = np.zeros((img.shape[0],img.shape[1]), np.uint8)
cv2.circle(circle_img,(x_center,y_center),radius,(255,255,255),-1)
rgbNum = cv2.mean(img, mask=img)[::-1]
rocks = []
distances = []
confidence=[]
blur = cv2.medianBlur(imgCircles, 5)
blur = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
#hist = cv2.equalizeHist(gray)
#blur = cv2.GaussianBlur(hist, (5,5), cv2.BORDER_DEFAULT)
height, width = blur.shape[:2]
# height and width of the image frame
print(height, width) # 906 1070
# ---- middle 8' rings
# Apply Hough Circle Transform
# param1 - threshold value for the Canny edge detector
# param2 - accumulator threshold
#minR = round(increment * 2.6)
#maxR = round(increment * 3.25)
#closest = round(increment * 2.5)
increment = height / 9.0
#minR = round(increment * 2.6)
#maxR = round(increment * 3.25)
#closest = round(increment * 2.5)
if height > 1000: # more of an issue with zoomed frame that cuts off outer rings
minR = round(height / 2.0)
maxR = round(height / 1.25)
else: # also issue with extra details at bottom
minR = round(height / 2.5)
maxR = round(height / 1.5)
closest = minR
print('Looking for Rings (min,max,closest): ', minR, maxR, closest)
circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 1, closest, param1=14, param2=25, minRadius=minR, maxRadius=maxR)
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
# Draw circles on the original image
for (cX, cY, cR) in circles:
cv2.circle(imgCircles, (cX, cY), cR, (0, 255, 0), 1)
print('circle radius: ' + str(cR))
print('Detected Ring Center: ',str(cX),str(cY))
break # exit after first one - it is the best
# Display the big circles
cv2_imshow(imgCircles)
else:
print('No Rings Found')
# --- look for rock shapes
# estimate image-independent parameters for rocks
# 906 1070
# rock limits
increment = increment / 4.0
minR = round(increment)
maxR = round(minR + increment/1.5)
closest = round(minR*1.5)
print('Rocks: ', minR, maxR, closest)
original = image.copy()
bgr = image.copy()
# ---- try for rocks
gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3,3), cv2.BORDER_DEFAULT)
# Apply Hough Circle Transform
circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 1, closest, param1=15, param2=30, minRadius=minR, maxRadius=maxR)
#circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, closest, param1=10, param2=30, minRadius=minR, maxRadius=maxR)
TEXT_FACE = cv2.FONT_HERSHEY_DUPLEX
TEXT_SCALE = 0.8
TEXT_THICKNESS = 1
# Convert the (x, y) coordinates and radius of the circles to integers
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
# Draw circles on the original image
i = 0
for (x, y, radius) in circles:
i = i + 1
cv2.circle(original, (x, y), radius, (0, 255, 0), 1)
#print('center of rock: ' + str(i), str(x), str(y), str(radius))
TEXT = "ROCK " + str(i)
text_size, _ = cv2.getTextSize(TEXT, TEXT_FACE, TEXT_SCALE, TEXT_THICKNESS)
text_origin = (x+2,y+2) # offset off from handle
(b, g, r) = image[y, x]
#print('rock ' + str(i) + ' radius: ' + str(radius))
#print("Pixel: Red: {}, Green: {}, Blue: {}".format(r, g, b))
colorBGR = (int(b), int(g), int(r))
rectX1 = int(x - radius)
rectX2 = int(rectX1+2.0*radius)
if(rectX1 < 0) or (rectX2 > width):
print(' X coords outside boundary' + str(rectX1), str(rectX2))
rectX1=rectX1 + 2
rectX2=rectX2 - 2
rectY1 = int(y - radius)
rectY2 = int(rectY1+2.0*radius)
if(rectY1 < 0) or (rectY2 > height):
print(' Y coords outside boundary' + str(rectY1), str(rectY2))
rectY1=rectY1 + 2
rectY2=rectY2 - 2
#print(rectX1, rectX2, rectY1, rectY2)
croppedImg = bgr[rectY1:rectY2, rectX1:rectX2]
print(TEXT)
cv2_imshow(croppedImg)
color = colorOfRock(croppedImg)
if color is not None:
print('ROCK COLOR: ' + color + '\n')
measure = (measureDistance(x, y, cX, cY))
if rockInRings(measure, cR):
rocks.append(color)
distances.append(measure)
else:
print('Ignoring Rock outside rings')
else:
print('ROCK COLOR N/A')
imagetxt = cv2.putText(original, TEXT, text_origin, TEXT_FACE, TEXT_SCALE, colorBGR, TEXT_THICKNESS, cv2.LINE_AA)
# Display the rocks
cv2_imshow(original)
else:
print('No Rocks Found')
print('rocks to measure:' + str(len(rocks)))
score = 0
scoringColor = ""
lastRock = ""
# Set confidence level for scoring based on radius of rings in pixels
confidenceLevel100 = cR *.0375
print('Max Confidence Distance: ', confidenceLevel100)
if (len(rocks) > 0):
distances, rocks = zip(*sorted(zip(distances,rocks)))
print(distances)
print(rocks)
for i in range(len(rocks)):
if (lastRock == ''):
scoringColor = rocks[i]
elif (rocks[i] != lastRock):
lastNonScoringRock = i
break
score = score + 1
lastRock = rocks[i]
distanceBetweenRings = abs(cR - distances[i])
if distanceBetweenRings >= confidenceLevel100:
scoringConfidence = 1.0
else:
scoringConfidence = (1.0 - abs(distanceBetweenRings - confidenceLevel100)/confidenceLevel100)
confidence.append(scoringConfidence)
# calculate scoring confidence based on distance between rocks
# and distance from scoring circle radius
if (scoringColor != ''):
print(scoringColor + ' is sitting ' + str(score))
estimatedScore = 'ESTIMATED that ' + scoringColor + ' is sitting ' + str(score)
distanceBetweenRocks = distances[lastNonScoringRock] - distances[lastNonScoringRock-1]
#print('Distance from last scoring rock: ', distanceBetweenRocks)
if distanceBetweenRocks >= confidenceLevel100:
scoringConfidence = 1.0
else:
# confidence level goes up as distance between grows
scoringConfidence = (1.0 - abs(distanceBetweenRocks - confidenceLevel100)/confidenceLevel100)
confidence.append(scoringConfidence)
if (lastNonScoringRock < len(rocks)): # check next rock in case it's close too
distanceBetweenRocks = abs(distances[lastNonScoringRock+1] - distances[lastNonScoringRock])
#print('Distance from next scoring rock: ', distanceBetweenRocks)
if distanceBetweenRocks >= confidenceLevel100:
scoringConfidence = 1.0
else:
# confidence level goes up as distance between grows
scoringConfidence = (1.0 - abs(distanceBetweenRocks - confidenceLevel100)/confidenceLevel100)
confidence.append(scoringConfidence)
confidenceLevel = ' ' + "{0:.1%}.format(sum(confidence)/len(confidence))"
else:
estimatedScore = 'No Rocks Found in House'
confidenceLevel = ''
print(estimatedScore + confidenceLevel)
Comments