-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
momenator
committed
Nov 9, 2019
1 parent
aaafb5a
commit 78f73fd
Showing
9 changed files
with
163 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
**/.DS_Store | ||
**/.DS_Store | ||
**/__pycache__ |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
from scipy.spatial import distance as dist | ||
from imutils import perspective | ||
from imutils import contours | ||
import numpy as np | ||
import imutils | ||
import cv2 | ||
|
||
|
||
# debugging | ||
def show_n_wait(title_name, image_input): | ||
cv2.imshow(title_name, image_input) | ||
cv2.waitKey(0) | ||
|
||
|
||
def midpoint(ptA, ptB): | ||
return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5) | ||
|
||
|
||
def resize_w_aspect_ratio(image, width=None, height=None, inter=cv2.INTER_AREA): | ||
dim = None | ||
(h, w) = image.shape[:2] | ||
|
||
if width is None and height is None: | ||
return image | ||
if width is None: | ||
r = height / float(h) | ||
dim = (int(w * r), height) | ||
else: | ||
r = width / float(w) | ||
dim = (width, int(h * r)) | ||
|
||
return cv2.resize(image, dim, interpolation=inter) | ||
|
||
def remove_shadows(image): | ||
pass | ||
|
||
|
||
def get_measurements(image_path, real_width, is_display=False): | ||
image = cv2.imread(image_path) | ||
|
||
# Resize image | ||
image_resized = resize_w_aspect_ratio(image, 800) | ||
gray = cv2.cvtColor(image_resized, cv2.COLOR_BGR2GRAY) | ||
# reduce noise by blurring! | ||
gray = cv2.GaussianBlur(gray, (7, 7), 0) | ||
|
||
# use canny edge detection to threshold the image | ||
# perform edge detection, then perform a dilation + erosion to | ||
# close gaps in between object edges | ||
# METHOD 1: canny edge detection uses adaptive threshold by default | ||
edges = cv2.Canny(gray, 50, 100) | ||
edges = cv2.dilate(edges, None, iterations=1) | ||
edges = cv2.erode(edges, None, iterations=1) | ||
|
||
# find contours in the edge map | ||
cnts = cv2.findContours(edges.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 | ||
|
||
measurements = [] | ||
|
||
# loop over the contours individually | ||
for c in cnts: | ||
# if the contour is not sufficiently large, ignore it | ||
if cv2.contourArea(c) < 250: | ||
continue | ||
|
||
# compute the rotated bounding box of the contour | ||
orig = image_resized.copy() | ||
box = cv2.minAreaRect(c) | ||
|
||
# check if cv2 contains overlap | ||
# what if they do? | ||
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) | ||
cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 0), 2) | ||
|
||
# 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-righ 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 | ||
cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)), | ||
(255, 0, 255), 2) | ||
cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)), | ||
(255, 0, 255), 2) | ||
|
||
# compute the Euclidean distance between the midpoints | ||
dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY)) | ||
dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY)) | ||
|
||
# if the pixels per metric has not been initialized, then | ||
# compute it as the ratio of pixels to supplied metric | ||
# (in this case, inches) | ||
if pixelsPerMetric is None: | ||
pixelsPerMetric = dB / real_width | ||
|
||
# compute the size of the object | ||
dimA = dA / pixelsPerMetric | ||
dimB = dB / pixelsPerMetric | ||
|
||
# draw the object sizes on the image | ||
cv2.putText(orig, "{:.3f}mm".format(dimA), | ||
(int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX, | ||
0.65, (255, 255, 255), 2) | ||
cv2.putText(orig, "{:.3f}mm".format(dimB), | ||
(int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX, | ||
0.65, (255, 255, 255), 2) | ||
|
||
if is_display: | ||
# show the output image | ||
cv2.imshow("Image", orig) | ||
cv2.waitKey(0) | ||
|
||
measurements.append((dimA, dimB)) | ||
|
||
# return all the measurements here! | ||
return measurements |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# python test.py --image images/example_01.png --width 0.955 | ||
import argparse | ||
import processing | ||
|
||
# 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)") | ||
args = vars(ap.parse_args()) | ||
|
||
measurements = processing.get_measurements(args["image"], args["width"]) | ||
print(measurements) |