Compare commits

...

39 Commits

Author SHA1 Message Date
Cole Deck 42ca50e4be Use the largest item instead of quitting when there is more than one 4 years ago
Cole Deck 052ea30f73 Move imports to after connection, add service files to pi 4 years ago
Cole Deck dbde062909 add input code 4 years ago
Cole Deck 26119e4056 tweak lighting, fix error message 4 years ago
Cole Deck eb479df122 small tweaks 4 years ago
Cole Deck 4eac873412 Add consistent error and info messages, tweak image blurring. Remove penny requirement. 4 years ago
Cole Deck 4ca4d0c3cd Make sure we can't take the log10 of 0 4 years ago
Cole Deck fa15964b98 Fix indentation, compile 4 years ago
Cole Deck 68c1f4dc1a Update magic numbers to be more consistent 4 years ago
Cole Deck 0cfa5cb1a8 Add "magic sorting" feature 4 years ago
Cole Deck 830a8e7727 Make it actually sort stuff and everything! 4 years ago
Cole Deck 4f59cc07d5 Delete 'cv2' 4 years ago
Cole Deck fc4e85a76d Delete 'sort' 4 years ago
Cole Deck 8a1b5c92ff Delete 'timeit' 4 years ago
Cole Deck 7217cb8b43 lower brightness 4 years ago
Cole Deck 52ef77e389 Use grbl's recommended command streaming setup 4 years ago
Cole Deck be2357b9b0 change cropping mechanism 4 years ago
Cole Deck d7a2e2602b Update 'run_detect.py' 4 years ago
Cole Deck dad1ac9e7a Update IP address 4 years ago
Cole Deck 4cd3bfc514 fix error in run_detect 4 years ago
Cole Deck cbda6a6cab remove unneeded script 4 years ago
Cole Deck 6524ba6cfe add netowrking between pi and laptop, basically add all the components together 4 years ago
Cole Deck 47eb71057f add bin position script, update neopixel code 4 years ago
Your Name 190dc73036 Fix syntax error 4 years ago
Your Name 5ec6b7a36a Add neopixel control script 4 years ago
Cole Deck 0feabe96e5 Update detection to be more reliable 4 years ago
Cole Deck bcfd61a7cf add sorting code, saving to file 4 years ago
Cole Deck e21628b608 output item list and scan 4 years ago
Cole Deck 129ad2a762 fix compiled version, improve contrast 4 years ago
Cole Deck af0410be8e compile to machine code again 4 years ago
Cole Deck 574bf7050c video capture and detection working now 4 years ago
Cole Deck c40d84705f stuff 4 years ago
Cole Deck 72d0015d8f imx135 support 4 years ago
Cole Deck bfa501b6db video stream success with pi camera 4 years ago
Cole Deck 32ae8d4559 add arm build of detect library 4 years ago
Cole Deck b1f7129b90 attempt to load video stream from pi directly (not working yet) 4 years ago
Cole Deck a8bc28443f compile to code, runner script 4 years ago
Cole Deck 140465d858 start conversion to support video frames and return a UUID 4 years ago
Cole Deck 58b90af9c3 fix bug with circle vs rectangle; basic detection for spacers, washers, and keps nuts 4 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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

@ -0,0 +1,13 @@
[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

@ -0,0 +1,15 @@
[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

@ -0,0 +1,13 @@
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
)

@ -0,0 +1,101 @@
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

@ -0,0 +1,7 @@
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

@ -0,0 +1,387 @@
# 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.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

@ -1,320 +1,2 @@
# 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("-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
# 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)
from logic import main # this comes from a compiled binary
main ()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

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

Loading…
Cancel
Save