Compare commits

..

2 Commits

Author SHA1 Message Date
Cole Deck a0046e71bc add another scan 5 years ago
Cole Deck c4bc1a6f16 attempt screw recognition 5 years ago

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,24 @@
/home/cole/item-sort/positives/IMG_10208.jpg 1 4 3 80 110
/home/cole/item-sort/positives/IMG_1155.jpg 1 1 1 30 32
/home/cole/item-sort/positives/IMG_1184.jpg 1 1 1 34 30
/home/cole/item-sort/positives/IMG_1287.jpg 1 2 2 35 29
/home/cole/item-sort/positives/IMG_13351.jpg 1 2 2 165 75
/home/cole/item-sort/positives/IMG_13857.jpg 1 2 5 90 142
/home/cole/item-sort/positives/IMG_1472.jpg 1 1 1 43 29
/home/cole/item-sort/positives/IMG_1584.jpg 1 1 1 45 31
/home/cole/item-sort/positives/IMG_1683.jpg 1 1 0 48 32
/home/cole/item-sort/positives/IMG_1764.jpg 1 2 1 38 39
/home/cole/item-sort/positives/IMG_1776.jpg 1 1 0 45 36
/home/cole/item-sort/positives/IMG_19530.jpg 1 4 4 120 149
/home/cole/item-sort/positives/IMG_2200.jpg 1 1 0 46 42
/home/cole/item-sort/positives/IMG_2772.jpg 1 0 0 83 32
/home/cole/item-sort/positives/IMG_2784.jpg 1 1 1 46 55
/home/cole/item-sort/positives/IMG_2800.jpg 1 1 1 53 48
/home/cole/item-sort/positives/IMG_3285.jpg 1 1 1 71 43
/home/cole/item-sort/positives/IMG_3604.jpg 1 1 0 104 33
/home/cole/item-sort/positives/IMG_4550.jpg 1 1 1 127 32
/home/cole/item-sort/positives/IMG_4879.jpg 1 2 2 37 114
/home/cole/item-sort/positives/IMG_7412.jpg 1 2 2 105 65
/home/cole/item-sort/positives/IMG_7654.jpg 1 4 2 80 84
/home/cole/item-sort/positives/IMG_7740.jpg 1 1 2 126 57

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@ -0,0 +1,38 @@
negatives/IMG_10285.jpg
negatives/IMG_1190.jpg
negatives/IMG_1225.jpg
negatives/IMG_1443.jpg
negatives/IMG_1482.jpg
negatives/IMG_17202.jpg
negatives/IMG_18939.jpg
negatives/IMG_19860.jpg
negatives/IMG_2116.jpg
negatives/IMG_2408.jpg
negatives/IMG_2475.jpg
negatives/IMG_2548.jpg
negatives/IMG_2597.jpg
negatives/IMG_2700.jpg
negatives/IMG_27048.jpg
negatives/IMG_2805.jpg
negatives/IMG_28583.jpg
negatives/IMG_30940.jpg
negatives/IMG_35235.jpg
negatives/IMG_3599.jpg
negatives/IMG_36040.jpg
negatives/IMG_36400.jpg
negatives/IMG_3666.jpg
negatives/IMG_3840.jpg
negatives/IMG_3844.jpg
negatives/IMG_40176.jpg
negatives/IMG_42840.jpg
negatives/IMG_5041.jpg
negatives/IMG_5184.jpg
negatives/IMG_5402.jpg
negatives/IMG_5700.jpg
negatives/IMG_5820.jpg
negatives/IMG_6014.jpg
negatives/IMG_6016.jpg
negatives/IMG_6240.jpg
negatives/IMG_6435.jpg
negatives/IMG_7482.jpg
negatives/IMG_9800.jpg

Binary file not shown.

Binary file not shown.

@ -1,13 +0,0 @@
[Unit]
Description=UDP Camera Stream
After=network.target auditd.service media-writable.mount
Requires=media-writable.mount
[Service]
ExecStart=/media/writable/run.sh
Restart=always
Type=idle
User=pi
[Install]
WantedBy=multi-user.target

@ -1,15 +0,0 @@
[Unit]
Description=Python Sorting Client
After=network.target auditd.service media-writable.mount
Requires=media-writable.mount
[Service]
ExecStart=/media/writable/item-sort/pi_client.py
Restart=always
Type=idle
User=root
StartLimitIntervalSec=0
RestartSec=5
WorkingDirectory=/media/writable/item-sort
[Install]
WantedBy=multi-user.target

@ -1,13 +0,0 @@
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
ext_modules = [
Extension("detect", ["detect.py"]),
#Extension("mymodule2", ["mymodule2.py"]),
# ... all your modules that need be compiled ...
]
setup(
name = 'Item Sorter',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)

@ -1,101 +0,0 @@
import math
import serial
import RPi.GPIO as gpio
import time
ser = serial.Serial('/dev/ttyUSB0', 115200)
gpio.setmode(gpio.BCM)
gpio.setup(13, gpio.OUT)
pwm = gpio.PWM(13, 100)
pwm.start(13)
verbose = True
print("[ INFO ] Initializing Grbl...")
ser.write(b'\r\n\r\n')
time.sleep(2)
ser.write(b'$RST=#\n')
time.sleep(1)
ser.flushInput()
time.sleep(0.25)
ser.write(b'$X\n')
print("[ INFO ] Grbl is ready.")
def goToBin(bin):
print("[ INFO ] Delivering item to bin: " + str(bin))
adjustedBin = math.floor(bin / 2)
if adjustedBin > 11:
print("[ INFO ] All bins full! Using overflow bin.")
bin = 0;
adjustedBin = 0;
distance = adjustedBin * 18
delay = 0.5 + 0.93 * adjustedBin
command = '$J=X-'
command += str(distance)
command += ' F2000'
print("[ INFO ] Sending command to Grbl: " + command)
command += '\n'
time.sleep(0.25)
ser.write(command.encode('utf-8'))
# s.write("$C\n")
while True:
grbl_out = str(ser.readline().strip()) # Wait for grbl response with carriage return
print(grbl_out)
if int(grbl_out.find('error')) >= 0 :
print("[ EXIT ] Grbl reported an error.")
quit()
elif int(grbl_out.find('ok')) >= 0 :
if verbose: print('[ INFO ] Grbl message: ',grbl_out)
break
print("[ INFO ] Waiting for " + str(delay) + " seconds.")
time.sleep(delay)
if bin % 2 == 0: # tilt to left
print("[ INFO ] Titling motor to left side.")
pwm.ChangeDutyCycle(5)
time.sleep(1)
pwm.ChangeDutyCycle(14)
else:
print("[ INFO ] Titling motor to right side.")
pwm.ChangeDutyCycle(25)
time.sleep(1)
pwm.ChangeDutyCycle(14)
time.sleep(1)
print("[ INFO ] Sending command to Grbl: G0 X0")
ser.write(b'$j=X0 F2000\n')
while True:
grbl_out = str(ser.readline().strip()) # Wait for grbl response with carriage return
if int(grbl_out.find('error')) >= 0 :
print("[ EXIT ] Grbl reported an error.")
quit()
elif int(grbl_out.find('ok')) >= 0 :
if verbose: print('[ INFO ] Grbl message: ',grbl_out)
break
print("[ INFO ] Waiting for " + str(delay) + " seconds.")
time.sleep(delay)
def stopInput():
command = '!'
command += '\n'
ser.write(command.encode('utf-8'))
print(command)
#command2 = str(0x85)
#ser.write(command2.encode('utf-8'))
#while True:
# grbl_out = str(ser.readline().strip()) # Wait for grbl response with carriage return
# if int(grbl_out.find('error')) >= 0 :
# print("[ EXIT ] Grbl reported an error.")
# quit()
# elif int(grbl_out.find('ok')) >= 0 :
# if verbose: print('[ INFO ] Grbl message: ',grbl_out)
# break
def startInput():
ser.write(b'$J=G91 Y-5000 F400\n')
print("intake")
#x = 0
#while True and x < 10:
#x += 1
#grbl_out = str(ser.readline().strip()) # Wait for grbl response with carriage return
#if int(grbl_out.find('error')) >= 0 :
# print("[ EXIT ] Grbl reported an error.")
# quit()
#elif int(grbl_out.find('ok')) >= 0 :
# if verbose: print('[ INFO ] Grbl message: ',grbl_out)
# break

@ -1,7 +0,0 @@
import board
import neopixel
pixels = neopixel.NeoPixel(board.D18, 23)
def ledOff():
pixels.fill((0,0,0))
def ledOn():
pixels.fill((2,2,2))

15228
detect.c

File diff suppressed because it is too large Load Diff

@ -1,387 +0,0 @@
# import the necessary packages
#from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
import math
import time
itemw = 0
itemh = 0
def midpoint(ptA, ptB):
return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)
def sizeVexScrew(iteml):
# Screw Sizing code
# subtract screw head size to find thread length
shead = 0.1
iteml -= shead
#print("Thread Length: " + str(iteml))
iteml *= 8
iteml = round(iteml)
iteml /= 8
return iteml
def sizeStandoff(iteml):
# Standoff Sizing code
iteml *= 2
iteml = round(iteml)
iteml /= 2
return iteml
def larger(a, b):
if a >= b:
return a
else:
return b
def smaller(a, b):
if a < b:
return a
else:
return b
def near(a, b, close):
if abs(a-b) < close:
return True
return False
def swap(a, b):
tmp = a
a = b
b = tmp
"""
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to the input image")
#ap.add_argument("-c", "--cascade", required=True,
# help="path to the cascade")
ap.add_argument("-w", "--width", type=float, required=True,
help="width of the left-most object in the image (in inches)")
ap.add_argument("-n", "--number", type=int, required=False,
help="object # to measure (from left to right)")
ap.add_argument("-s", "--show", action="store_true",
help="show on the screen")
args = vars(ap.parse_args())
args2 = ap.parse_args()"""
def detect(calibration_width, img_file, show, quick):
list = []
#if type(args["number"]) == type(selected):
# selected = args["number"]
# load the image, convert it to grayscale, and blur it slightly
image = None
#print(str(type(img_file)))
if str(type(img_file)) == "<class 'numpy.ndarray'>":
image = img_file.copy()
else:
image = cv2.imread(img_file)
#image = img_file.copy()
image = cv2.resize(image, (math.floor(image.shape[1]*0.5), math.floor(image.shape[0]*0.5)))
#image = cv2.resize(image, (1000, int(image.shape[0]/image.shape[1] * 1000)), interpolation=cv2.INTER_NEAREST)
if show and not quick:
cv2.namedWindow("Item Sorter")
cv2.imshow("Item Sorter", image)
cv2.waitKey(0)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=2)
edged = cv2.erode(edged, None, iterations=2)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)
edged = cv2.dilate(edged, None, iterations=2)
#edged = cv2.erode(edged, None, iterations=1)
#edged = cv2.dilate(edged, None, iterations=1)
if show and not quick:
cv2.imshow("Item Sorter", edged)
cv2.waitKey(0)
# find contours in the edge map
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# sort the contours from left-to-right and initialize the
# 'pixels per metric' calibration variable
#(cnts, _) = contours.sort_contours(cnts)
pixelsPerMetric = None
num = 0
# Calibration loop
"""
for c in cnts:
# if the contour is not sufficiently large, ignore it
if cv2.contourArea(c) < 100:
continue
# compute the rotated bounding box of the contour
orig = image.copy()
box = cv2.minAreaRect(c)
# xpos,ypos,w,h = cv2.boundingRect(c)
# crop_img = orig[ypos:ypos+h, xpos:xpos+w]
# cv2.imwrite("object_images/IMG_" + str(w*h) + ".jpg", crop_img) # create training images
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="int")
#box = perspective.order_points(box)
(tl, tr, br, bl) = box
(tltrX, tltrY) = midpoint(tl, tr)
(blbrX, blbrY) = midpoint(bl, br)
(tlblX, tlblY) = midpoint(tl, bl)
(trbrX, trbrY) = midpoint(tr, br)
dA = np.linalg.norm(np.array((tltrX, tltrY, 0)) -
np.array((blbrX, blbrY, 0)))
dB = np.linalg.norm(np.array((tlblX, tlblY, 0)) -
np.array((trbrX, trbrY, 0)))
area_box = dA * dB
(x, y), radius = cv2.minEnclosingCircle(c)
area_contour = cv2.contourArea(c)
area_circle = math.pi * pow(radius, 2)
boxiness = area_contour / area_box
circleness = area_contour / area_circle
circular = False
rectangular = False
if boxiness > circleness:
rectangular = True
cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)
else:
circular = True
cv2.circle(orig, (int(x), int(y)), int(radius), (0, 255, 0), 3)
mask = np.zeros(gray.shape, np.uint8)
cv2.drawContours(mask, [c], 0, 255, -1)
#pixelpoints = np.transpose(np.nonzero(mask))
hsv = cv2.cvtColor(orig, cv2.COLOR_BGR2HSV)
mean_val = cv2.mean(hsv, mask=mask)
#print(str(mean_val[0]))
#print(", " + str(mean_val[0]/mean_val[2]))
#print(", " + str(mean_val[2]/mean_val[1]))
if pixelsPerMetric is None and circular is True and near(mean_val[0], 16, 4.5):
# and near(mean_val[0], 63, 40) is True and near(mean_val[1], 108, 40) is True and near(mean_val[2], 104, 40) is True:
pixelsPerMetric = smaller(dA, dB) / calibration_width
continue
"""
pixelsPerMetric = 25
orig = image.copy()
objtype = "Object"
objname = ""
c = None
# loop over the contours individually
if len(cnts) == 0:
return ((),edged)
area = cv2.contourArea(cnts[0])
if area < 400:
area = 0
for contour in cnts:
if cv2.contourArea(contour) >= area and cv2.contourArea(contour) > 400:
area = cv2.contourArea(contour)
c = contour
if c is not None:
#orig = image.copy()
num += 1
# if the contour is not sufficiently large, ignore it
#pixelsPerMetric = 75
#if cv2.contourArea(c) < 300 or pixelsPerMetric is None:
#continue
# compute the rotated bounding box of the contour
box = cv2.minAreaRect(c)
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="int")
# unpack the ordered bounding box, then compute the midpoint
# between the top-left and top-right coordinates, followed by
# the midpoint between bottom-left and bottom-right coordinates
(tl, tr, br, bl) = box
(tltrX, tltrY) = midpoint(tl, tr)
(blbrX, blbrY) = midpoint(bl, br)
# compute the midpoint between the top-left and top-right points,
# followed by the midpoint between the top-right and bottom-right
(tlblX, tlblY) = midpoint(tl, bl)
(trbrX, trbrY) = midpoint(tr, br)
# compute the Euclidean distance between the midpoints
dA = np.linalg.norm(np.array((tltrX, tltrY, 0)) -
np.array((blbrX, blbrY, 0)))
dB = np.linalg.norm(np.array((tlblX, tlblY, 0)) -
np.array((trbrX, trbrY, 0)))
dimA = dA / pixelsPerMetric
dimB = dB / pixelsPerMetric
# Item detection
area_box = dA * dB
(x, y), radius = cv2.minEnclosingCircle(c)
area_contour = cv2.contourArea(c)
area_circle = math.pi * pow(radius, 2)
boxiness = area_contour / area_box
circleness = area_contour / area_circle
circular = False
rectangular = False
if boxiness > circleness:
rectangular = True
#cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)
else:
circular = True
cv2.circle(orig, (int(x), int(y)), int(radius), (255, 0, 0), 2)
objtype = "Object"
itemw = larger(dimA, dimB)
itemwr = itemw
itemwr *= 8
itemwr = round(itemwr)
itemwr /= 8
itemh = smaller(dimA, dimB)
itemhr = itemh
itemhr *= 16
itemhr = round(itemhr)
itemhr /= 16
if circular and itemwr == 0.75:
objtype = "Penny"
iteml = 0
"""else:
if circular and near(radius * 2 / pixelsPerMetric, 0.4, 0.03):
# Keps nut or spacer
objtype = "Spacer"
mask = np.zeros(gray.shape, np.uint8)
cv2.drawContours(mask, [c], 0, 255, -1)
#pixelpoints = np.transpose(np.nonzero(mask))
hsv = cv2.cvtColor(orig, cv2.COLOR_BGR2HSV)
mean_val = cv2.mean(hsv, mask=mask)
mean_rgb = cv2.mean(orig, mask=mask)
if near(mean_rgb[2], 59, 3) and near(mean_val[1], 85, 5): #and near(mean_val[2], 78, 5):
objtype = "Keps Nut"
print(str(mean_rgb[2]) + objtype + str(mean_val[1]))
elif circular and near(radius / pixelsPerMetric, 0.23, 0.02):
objtype = "Washer"
#print(str(radius * 2 / pixelsPerMetric) + objtype)
epsilon = 3 # 0.02*cv2.arcLength(c,True)
# print(str(epsilon))
approx = cv2.approxPolyDP(c, epsilon, True)
hull = cv2.convexHull(approx, returnPoints=False)
hull2 = cv2.convexHull(c)
defects = cv2.convexityDefects(c, hull)
#print(str(defects.size) + " match")
cv2.drawContours(orig, (hull2), -1, (0, 0, 255), 3)
cv2.drawContours(orig, (approx), -1, (255, 0, 0), 3)
convexness = area_contour / cv2.contourArea(hull2)
#print(str(convexness) + " % fill")
# if not cv2.isContourConvex(approx):
# if cv2.matchShapes(hull, c, 1, 0.0) > 1:
if defects is not None and defects.size > 5 and (convexness < 0.9 or boxiness < 0.75) and rectangular:
objtype = "Screw"
iteml = larger(dimA, dimB)
#print("Screw Length (RAW): " + str(iteml))
iteml = sizeVexScrew(radius * 2 / pixelsPerMetric)
#print("Rounded Length: " + str(iteml))
else:
if itemhr == 0.3125 and rectangular:
objtype = "Standoff"
iteml = sizeStandoff(itemw)
if itemhr == 0.1875 and rectangular:
objtype = "Axle"
iteml = (radius * 2 / pixelsPerMetric + itemw) / 2
"""
rows, cols = orig.shape[:2]
[vx, vy, xx, yy] = cv2.fitLine(c, cv2.DIST_L2, 0, 0.01, 0.01)
lefty = int((-xx*vy/vx) + yy)
righty = int(((cols-xx)*vy/vx)+yy)
# cv2.line(orig,(cols-1,righty),(0,lefty),(0,255,0),2)
slope = (lefty - righty) / (1 - cols)
angle = math.atan(slope)
xpos = x - math.cos(angle) * radius
ypos = y - math.sin(angle) * radius
xpos2 = x + math.cos(angle) * radius
ypos2 = y + math.sin(angle) * radius
if xpos > xpos2:
swap(xpos, xpos2)
swap(ypos, ypos2)
if rectangular:
cv2.line(orig, (int(xpos), int(ypos)),
(int(xpos2), int(ypos2)), (255, 127, 0), 2)
# print(str(iteml))
# draw the object sizes on the image
# cv2.putText(orig, "{:.5f}in".format(itemhr),
# (int(trbrX + 20), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX,
# 0.65, (255, 255, 255), 2)
if objtype != "Penny":
objtype = magicSort(c)
if objtype == "Object":
objtype = magicSort(c)
output = "{:.2f}in".format(itemw) + " x {:.2f}in".format(itemh)
if circular:
cv2.putText(orig, str(objtype),
(int(x - 25), int(y + radius + 20)
), cv2.FONT_HERSHEY_SIMPLEX,
0.6, (50, 50, 220), 2)
else:
cv2.putText(orig, str(objtype),
(int(xpos2 + 10), int(ypos2 + 20)
), cv2.FONT_HERSHEY_SIMPLEX,
0.6, (50, 50, 220), 2)
output = ""
objname = objtype;
"""
if objtype == "Screw" or objtype == "Standoff":
output = str(iteml) + "in"
objname += str(iteml)
if objtype == "Axle":
output = "{:.2f}in".format(iteml)
objname += str(itemwr)
#print(objname)
"""
list.append(objname)
if circular:
cv2.putText(orig, output, # print data
(int(x - 25), int(y + radius + 40)
), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (50, 50, 220), 1)
else:
cv2.putText(orig, output, # print data
(int(xpos2 + 10), int(ypos2 + 40)
), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (50, 50, 220), 1)
# show the output image
if show and not quick:
cv2.imshow("Item Sorter", orig)
#cv2.waitKey(1)
if not quick:
cv2.waitKey(0)
return (list, edged)
def magicSort(contour):
moments = cv2.moments(contour)
humoments = cv2.HuMoments(moments)
#humoments[6] = abs(humoments[6]) #it's possible for the last number to change sign if item is mirrored
#magicNumber1 = 0
#magicNumber2 = 0
name = "Object"
for i in range(0,7):
if humoments[i] == 0:
humoments[i] = 0.1;
humoments[i] = -1 * math.copysign(1.0, humoments[i]) * math.log10(abs(humoments[i]))
if i > 1:
humoments[i] = int(round(humoments[i][0] / 8) * 8)
if i != 4 and i != 6:
name += ", " + str(abs(int(humoments[i][0])))
#magicNumber1 += abs(humoments[i][0])
else:
humoments[i] = int(round(humoments[i][0] * 4) * 16)
name += ", " + str(abs(int(humoments[i][0])))
#magicNumber2 += abs(humoments[i][0])
#magicNumber += humoments[i][0]
#print(str(humoments))
#print(magicNumber)
#name = "Unknown: " + str(int(magicNumber1)) + ", " + str(int(magicNumber2))
#print(name)
return name

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 MiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

@ -1,2 +1,333 @@
from logic import main # this comes from a compiled binary # import the necessary packages
main () #from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2
import math
import time
itemw = 0
itemh = 0
def midpoint(ptA, ptB):
return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)
def sizeVexScrew(iteml):
# Screw Sizing code
# subtract screw head size to find thread length
shead = 0.1
iteml -= shead
#print("Thread Length: " + str(iteml))
iteml *= 8
iteml = round(iteml)
iteml /= 8
return iteml
def sizeStandoff(iteml):
# Standoff Sizing code
iteml *= 2
iteml = round(iteml)
iteml /= 2
return iteml
def larger(a, b):
if a >= b:
return a
else:
return b
def smaller(a, b):
if a < b:
return a
else:
return b
def near(a, b, close):
if abs(a-b) < close:
return True
return False
def swap(a, b):
tmp = a
a = b
b = tmp
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to the input image")
ap.add_argument("-c", "--cascade", required=True,
help="path to the cascade")
ap.add_argument("-w", "--width", type=float, required=True,
help="width of the left-most object in the image (in inches)")
ap.add_argument("-n", "--number", type=int, required=False,
help="object # to measure (from left to right)")
ap.add_argument("-s", "--show", action="store_true",
help="show on the screen")
args = vars(ap.parse_args())
args2 = ap.parse_args()
selected = 2
if type(args["number"]) == type(selected):
selected = args["number"]
# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread(args["image"])
#image = cv2.resize(image, (int(image.shape[1]*1), int(image.shape[0]*1)))
image = cv2.resize(image, (1000, int(
image.shape[0]/image.shape[1] * 1000)), interpolation=cv2.INTER_NEAREST)
if args2.show:
cv2.namedWindow("Item Sorter")
cv2.imshow("Item Sorter", image)
cv2.waitKey(0)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
#edged = cv2.erode(edged, None, iterations=1)
if args2.show:
cv2.imshow("Item Sorter", edged)
cv2.waitKey(0)
# find contours in the edge map
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# sort the contours from left-to-right and initialize the
# 'pixels per metric' calibration variable
#(cnts, _) = contours.sort_contours(cnts)
pixelsPerMetric = None
num = 0
cascade = cv2.CascadeClassifier()
if not cascade.load(cv2.samples.findFile(args2.cascade)):
print('--(!)Error loading face cascade')
exit(0)
screws = cascade.detectMultiScale(image)
frame = image.copy()
for (x,y,w,h) in screws:
center = (x + w//2, y + h//2)
frame = cv2.ellipse(frame, center, (w//2, h//2), 0, 0, 360, (255, 0, 255), 4)
if args2.show:
cv2.imshow("Item Sorter", frame)
cv2.waitKey(0)
# Calibration loop
for c in cnts:
# if the contour is not sufficiently large, ignore it
if cv2.contourArea(c) < 100:
continue
# compute the rotated bounding box of the contour
orig = image.copy()
box = cv2.minAreaRect(c)
# xpos,ypos,w,h = cv2.boundingRect(c)
# crop_img = orig[ypos:ypos+h, xpos:xpos+w]
# cv2.imwrite("object_images/IMG_" + str(w*h) + ".jpg", crop_img) # create training images
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="int")
#box = perspective.order_points(box)
(tl, tr, br, bl) = box
(tltrX, tltrY) = midpoint(tl, tr)
(blbrX, blbrY) = midpoint(bl, br)
(tlblX, tlblY) = midpoint(tl, bl)
(trbrX, trbrY) = midpoint(tr, br)
dA = np.linalg.norm(np.array((tltrX, tltrY, 0)) -
np.array((blbrX, blbrY, 0)))
dB = np.linalg.norm(np.array((tlblX, tlblY, 0)) -
np.array((trbrX, trbrY, 0)))
area_box = dA * dB
(x, y), radius = cv2.minEnclosingCircle(c)
area_contour = cv2.contourArea(c)
area_circle = math.pi * pow(radius, 2)
boxiness = area_contour / area_box
circleness = area_contour / area_circle
circular = False
rectangular = False
if boxiness > circleness:
rectangular = True
cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)
else:
circular = True
cv2.circle(orig, (int(x), int(y)), int(radius), (0, 255, 0), 2)
mask = np.zeros(gray.shape, np.uint8)
cv2.drawContours(mask, [c], 0, 255, -1)
#pixelpoints = np.transpose(np.nonzero(mask))
hsv = cv2.cvtColor(orig, cv2.COLOR_BGR2HSV)
mean_val = cv2.mean(hsv, mask=mask)
print(str(mean_val[0]))
#print(", " + str(mean_val[0]/mean_val[2]))
#print(", " + str(mean_val[2]/mean_val[1]))
if pixelsPerMetric is None and circular is True and near(mean_val[0], 16, 4.5):
# and near(mean_val[0], 63, 40) is True and near(mean_val[1], 108, 40) is True and near(mean_val[2], 104, 40) is True:
pixelsPerMetric = smaller(dA, dB) / args["width"]
orig = image.copy()
# loop over the contours individually
for c in cnts:
#orig = image.copy()
num += 1
# if the contour is not sufficiently large, ignore it
if cv2.contourArea(c) < 100 or pixelsPerMetric is None:
continue
# compute the rotated bounding box of the contour
box = cv2.minAreaRect(c)
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="int")
# order the points in the contour such that they appear
# in top-left, top-right, bottom-right, and bottom-left
# order, then draw the outline of the rotated bounding
# box
#box = perspective.order_points(box)
# loop over the original points and draw them
# for (x, y) in box:
#cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1)
# unpack the ordered bounding box, then compute the midpoint
# between the top-left and top-right coordinates, followed by
# the midpoint between bottom-left and bottom-right coordinates
(tl, tr, br, bl) = box
(tltrX, tltrY) = midpoint(tl, tr)
(blbrX, blbrY) = midpoint(bl, br)
# compute the midpoint between the top-left and top-right points,
# followed by the midpoint between the top-right and bottom-right
(tlblX, tlblY) = midpoint(tl, bl)
(trbrX, trbrY) = midpoint(tr, br)
# draw the midpoints on the image
#cv2.circle(orig, (int(tltrX), int(tltrY)), 5, (255, 0, 0), -1)
#cv2.circle(orig, (int(blbrX), int(blbrY)), 5, (255, 0, 0), -1)
#cv2.circle(orig, (int(tlblX), int(tlblY)), 5, (255, 0, 0), -1)
#cv2.circle(orig, (int(trbrX), int(trbrY)), 5, (255, 0, 0), -1)
# draw lines between the midpoints
# compute the Euclidean distance between the midpoints
dA = np.linalg.norm(np.array((tltrX, tltrY, 0)) -
np.array((blbrX, blbrY, 0)))
dB = np.linalg.norm(np.array((tlblX, tlblY, 0)) -
np.array((trbrX, trbrY, 0)))
dimA = dA / pixelsPerMetric
dimB = dB / pixelsPerMetric
if num == selected or args2.show:
area_box = dA * dB
(x, y), radius = cv2.minEnclosingCircle(c)
area_contour = cv2.contourArea(c)
area_circle = math.pi * pow(radius, 2)
boxiness = area_contour / area_box
circleness = area_contour / area_circle
circular = False
rectangular = False
if boxiness > circleness:
rectangular = True
#cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2)
else:
circular = True
cv2.circle(orig, (int(x), int(y)), int(radius), (0, 255, 0), 1)
objtype = "Unknown"
itemw = larger(dimA, dimB)
itemwr = itemw
itemwr *= 8
itemwr = round(itemwr)
itemwr /= 8
itemh = smaller(dimA, dimB)
itemhr = itemh
itemhr *= 16
itemhr = round(itemhr)
itemhr /= 16
if circular and itemwr == 0.75:
objtype = "Penny"
iteml = 0
else:
epsilon = 3 # 0.02*cv2.arcLength(c,True)
# print(str(epsilon))
approx = cv2.approxPolyDP(c, epsilon, True)
hull = cv2.convexHull(approx, returnPoints=False)
hull2 = cv2.convexHull(c)
defects = cv2.convexityDefects(c, hull)
#print(str(defects.size) + " match")
cv2.drawContours(orig, (hull2), -1, (0, 0, 255), 3)
cv2.drawContours(orig, (approx), -1, (255, 0, 0), 3)
convexness = area_contour / cv2.contourArea(hull2)
#print(str(convexness) + " % fill")
# if not cv2.isContourConvex(approx):
# if cv2.matchShapes(hull, c, 1, 0.0) > 1:
if defects.size > 5 and (convexness < 0.9 or boxiness < 0.75):
objtype = "Screw"
iteml = larger(dimA, dimB)
#print("Screw Length (RAW): " + str(iteml))
iteml = sizeVexScrew(radius * 2 / pixelsPerMetric)
#print("Rounded Length: " + str(iteml))
else:
if itemhr == 0.3125:
objtype = "Standoff"
iteml = sizeStandoff(itemw)
if itemhr == 0.1875:
objtype = "Axle"
iteml = (radius * 2 / pixelsPerMetric + itemw) / 2
rows, cols = orig.shape[:2]
[vx, vy, xx, yy] = cv2.fitLine(c, cv2.DIST_L2, 0, 0.01, 0.01)
lefty = int((-xx*vy/vx) + yy)
righty = int(((cols-xx)*vy/vx)+yy)
# cv2.line(orig,(cols-1,righty),(0,lefty),(0,255,0),2)
slope = (lefty - righty) / (1 - cols)
angle = math.atan(slope)
xpos = x - math.cos(angle) * radius
ypos = y - math.sin(angle) * radius
xpos2 = x + math.cos(angle) * radius
ypos2 = y + math.sin(angle) * radius
if xpos > xpos2:
swap(xpos, xpos2)
swap(ypos, ypos2)
if rectangular:
cv2.line(orig, (int(xpos), int(ypos)),
(int(xpos2), int(ypos2)), (0, 255, 0), 1)
# print(str(iteml))
# draw the object sizes on the image
if args2.show:
# cv2.putText(orig, "{:.5f}in".format(itemhr),
# (int(trbrX + 20), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX,
# 0.65, (255, 255, 255), 2)
cv2.putText(orig, str(objtype),
(int(xpos2 + 10), int(ypos2 + 20)
), cv2.FONT_HERSHEY_SIMPLEX,
0.65, (255, 255, 255), 2)
output = ""
if objtype == "Unknown":
output = "{:.2f}in".format(itemw) + " x {:.2f}in".format(itemh)
if objtype == "Screw" or objtype == "Standoff":
output = str(iteml) + "in"
if objtype == "Axle":
output = "{:.2f}in".format(iteml)
cv2.putText(orig, output, # print data
(int(xpos2 + 10), int(ypos2 + 40)
), cv2.FONT_HERSHEY_SIMPLEX,
0.65, (255, 255, 255), 2)
# show the output image
cv2.imshow("Item Sorter", orig)
cv2.waitKey(25)
cv2.waitKey(0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save