2024-09-22 00:22:42 +00:00
|
|
|
import cv2
|
|
|
|
import imutils
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
import numpy as np
|
|
|
|
from mpl_toolkits.mplot3d import Axes3D
|
|
|
|
from matplotlib import cm
|
|
|
|
from matplotlib import colors
|
|
|
|
|
|
|
|
RESIZE_RATIO = 0.07
|
2024-09-22 03:11:40 +00:00
|
|
|
FILENAMES = ["testimg.jpg", "testimg2.jpg", "testimg3.jpg"]
|
2024-09-22 00:22:42 +00:00
|
|
|
|
|
|
|
def shrink(img):
|
|
|
|
return cv2.resize(img, None, fx=RESIZE_RATIO, fy=RESIZE_RATIO)
|
|
|
|
|
|
|
|
def noop(*x):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def darken_color(img, sat=20, val=225):
|
|
|
|
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
|
|
|
h, s, v = cv2.split(hsv)
|
|
|
|
s[s > sat] = 255
|
|
|
|
v[v < val] = 0
|
|
|
|
hsv2 = cv2.merge((h, s, v))
|
|
|
|
|
|
|
|
return cv2.cvtColor(hsv2, cv2.COLOR_HSV2BGR)
|
|
|
|
|
|
|
|
def interactive_edge(filename):
|
|
|
|
img = shrink(cv2.imread(filename))
|
|
|
|
|
|
|
|
cv2.createTrackbar("threshold1", "image", 5, 50, noop)
|
|
|
|
cv2.createTrackbar("threshold2", "image", 20, 50, noop)
|
|
|
|
|
2024-09-22 03:11:40 +00:00
|
|
|
while handle_key():
|
2024-09-22 00:22:42 +00:00
|
|
|
threshold1 = cv2.getTrackbarPos("threshold1", "image")
|
|
|
|
threshold2 = cv2.getTrackbarPos("threshold2", "image")
|
|
|
|
edged = edge_img(img, threshold1 * 10, threshold2 * 10)
|
|
|
|
# contours = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
|
|
# contours = imutils.grab_contours(contours)
|
|
|
|
# shapes = cv2.drawContours(edged.copy(), contours, -1, (255, 255, 255), -1)
|
|
|
|
cv2.imshow("image", edged)
|
|
|
|
|
|
|
|
|
|
|
|
def edge_img(orig, t1=50, t2=200):
|
|
|
|
img = cv2.GaussianBlur(orig.copy(), (5, 5), 0)
|
|
|
|
img = darken_color(img)
|
|
|
|
|
|
|
|
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
|
|
gray_blurred = cv2.GaussianBlur(gray, (5, 5), 0)
|
|
|
|
return cv2.Canny(gray_blurred, t1, t2)
|
|
|
|
|
|
|
|
|
|
|
|
def approximate_contour(c):
|
|
|
|
peri = cv2.arcLength(c, True)
|
|
|
|
return cv2.approxPolyDP(c, 0.02 * peri, True)
|
|
|
|
|
|
|
|
|
|
|
|
def group_contours(contours):
|
|
|
|
remaining = contours.copy()
|
|
|
|
groups = []
|
|
|
|
while len(remaining) > 0:
|
|
|
|
group = [remaining[0]]
|
|
|
|
groups.append(group)
|
|
|
|
grew = False
|
|
|
|
remaining = remaining[1:]
|
|
|
|
while True:
|
|
|
|
nextremaining = []
|
|
|
|
l, t, r, b = cv2.boundingRect(remaining[0])
|
|
|
|
for c in remaining:
|
|
|
|
l2, t2, r2, b2 = cv2.boundingRect(c)
|
|
|
|
if r2 < l or l2 > r or t2 > b or b2 < t:
|
|
|
|
# they do not intersect
|
|
|
|
nextremaining.append(c)
|
|
|
|
else:
|
|
|
|
# they do intersect
|
|
|
|
grew = True
|
|
|
|
group.append(c)
|
|
|
|
l = min(l, l2)
|
|
|
|
t = min(t, t2)
|
|
|
|
r = max(r, r2)
|
|
|
|
b = max(b, b2)
|
|
|
|
remaining = nextremaining
|
|
|
|
if not grew:
|
|
|
|
break
|
|
|
|
return groups
|
|
|
|
|
2024-09-22 03:11:40 +00:00
|
|
|
def rects_intersect(rect1, rect2):
|
|
|
|
l1, t1, w1, h1 = rect1
|
|
|
|
l2, t2, w2, h2 = rect2
|
|
|
|
r1 = l1 + w1
|
|
|
|
r2 = l2 + w2
|
|
|
|
b1 = t1 + h1
|
|
|
|
b2 = t2 + h2
|
|
|
|
intersect = not (r2 < l1 or l2 > r1 or t2 > b1 or b2 < t1)
|
|
|
|
print("1:", rect1, "2:", rect2, intersect)
|
|
|
|
return intersect
|
|
|
|
|
|
|
|
def merge_contours(contours):
|
|
|
|
while True:
|
|
|
|
# take the convex hull of all discovered contours, then merge any contours whose bounding rectangles
|
|
|
|
# overlap. do this repeatedly until you have only completely non-overlapping islands.
|
|
|
|
consumed = set()
|
|
|
|
prev_contours = [cv2.convexHull(c) for c in contours]
|
|
|
|
contours = []
|
|
|
|
for i, contour in enumerate(prev_contours):
|
|
|
|
if not (i in consumed):
|
|
|
|
for iother, other in enumerate(prev_contours[i+1:], i+1):
|
|
|
|
if rects_intersect(cv2.boundingRect(contour), cv2.boundingRect(other)):
|
|
|
|
contour = np.vstack([contour, other])
|
|
|
|
consumed.add(iother)
|
|
|
|
contours.append(contour)
|
|
|
|
if len(consumed) == 0:
|
|
|
|
return contours
|
|
|
|
|
|
|
|
def contours_to_boxes(contours):
|
|
|
|
boxes = []
|
|
|
|
for c in contours:
|
|
|
|
# calculate two box contours:
|
|
|
|
# 1. axis-aligned bounding rectangle of the minimum enclosing circle
|
|
|
|
# 2. minimum area rotated rectangle
|
|
|
|
# use the smaller of the two (preferring #1 if they are of similar size)
|
|
|
|
(x, y), r = cv2.minEnclosingCircle(c)
|
|
|
|
box1 = np.intp([(x - r, y - r), (x + r, y - r), (x + r, y + r), (x - r, y + r)])
|
|
|
|
box2 = np.intp(cv2.boxPoints(cv2.minAreaRect(c)))
|
|
|
|
if cv2.contourArea(box1) < cv2.contourArea(box2) * 1.1:
|
|
|
|
boxes.append(box1)
|
|
|
|
else:
|
|
|
|
boxes.append(box2)
|
|
|
|
return boxes
|
2024-09-22 00:22:42 +00:00
|
|
|
|
|
|
|
def interactive_contour(filename):
|
|
|
|
orig = cv2.imread(filename)
|
|
|
|
orig = shrink(orig)
|
|
|
|
|
|
|
|
edge = edge_img(orig)
|
|
|
|
contours = cv2.findContours(edge, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
|
|
contours = imutils.grab_contours(contours)
|
2024-09-22 03:11:40 +00:00
|
|
|
contours = merge_contours(contours)
|
|
|
|
contours = contours_to_boxes(contours)
|
2024-09-22 00:22:42 +00:00
|
|
|
|
|
|
|
cv2.createTrackbar("contour", "image", 0, len(contours) - 1, noop)
|
|
|
|
|
2024-09-22 03:11:40 +00:00
|
|
|
while handle_key():
|
2024-09-22 00:22:42 +00:00
|
|
|
icontour = cv2.getTrackbarPos("contour", "image")
|
|
|
|
img = cv2.drawContours(orig.copy(), contours, icontour, (255, 0, 255), 3)
|
|
|
|
cv2.imshow("image", img)
|
|
|
|
|
|
|
|
|
|
|
|
def get_pixel_colors(rgbimg):
|
|
|
|
pixel_colors = rgbimg.reshape((np.shape(rgbimg)[0] * np.shape(rgbimg)[1], 3))
|
|
|
|
norm = colors.Normalize(vmin=-1.0, vmax=1.0)
|
|
|
|
norm.autoscale(pixel_colors)
|
|
|
|
return norm(pixel_colors).tolist()
|
|
|
|
|
|
|
|
|
|
|
|
def plot_hsv(rgbimg):
|
|
|
|
hsv = cv2.cvtColor(rgbimg.copy(), cv2.COLOR_BGR2HSV)
|
|
|
|
|
|
|
|
h, s, v = cv2.split(hsv)
|
|
|
|
fig = plt.figure()
|
|
|
|
axis = fig.add_subplot(1, 1, 1, projection="3d")
|
|
|
|
axis.scatter(
|
|
|
|
h.flatten(),
|
|
|
|
s.flatten(),
|
|
|
|
v.flatten(),
|
|
|
|
facecolors=get_pixel_colors(rgbimg),
|
|
|
|
marker=".",
|
|
|
|
)
|
|
|
|
axis.set_xlabel("Hue")
|
|
|
|
axis.set_ylabel("Sat")
|
|
|
|
axis.set_zlabel("Val")
|
|
|
|
plt.show()
|
|
|
|
|
|
|
|
|
|
|
|
def interactive_darken(filename):
|
|
|
|
img = cv2.imread(filename)
|
|
|
|
img = shrink(img)
|
|
|
|
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
|
|
|
|
|
|
|
cv2.createTrackbar("Sat", "image", 30, 255, noop)
|
|
|
|
cv2.createTrackbar("Val", "image", 225, 255, noop)
|
|
|
|
|
2024-09-22 03:11:40 +00:00
|
|
|
while handle_key():
|
2024-09-22 00:22:42 +00:00
|
|
|
sat = cv2.getTrackbarPos("Sat", "image")
|
|
|
|
val = cv2.getTrackbarPos("Val", "image")
|
|
|
|
|
|
|
|
h, s, v = cv2.split(hsv)
|
|
|
|
s[s > sat] = 255
|
|
|
|
v[v < val] = 0
|
|
|
|
hsv2 = cv2.merge((h, s, v))
|
|
|
|
|
|
|
|
bgr = cv2.cvtColor(hsv2, cv2.COLOR_HSV2BGR)
|
|
|
|
cv2.imshow("image", bgr)
|
|
|
|
|
2024-09-22 03:11:40 +00:00
|
|
|
selected_file = FILENAMES[0]
|
|
|
|
selected_tool = interactive_contour
|
|
|
|
def handle_key():
|
|
|
|
global selected_file, selected_tool
|
|
|
|
key = cv2.waitKey(1) & 0xff
|
|
|
|
if key == 27: # esc
|
|
|
|
selected_tool = None
|
|
|
|
elif key >= ord('0') and key <= ord('9'):
|
|
|
|
selected_file = FILENAMES[key - ord('0')]
|
|
|
|
elif key == ord("e"):
|
|
|
|
selected_tool = interactive_edge
|
|
|
|
elif key == ord("d"):
|
|
|
|
selected_tool = interactive_darken
|
|
|
|
elif key == ord("c"):
|
|
|
|
selected_tool = interactive_contour
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def tool_loop():
|
|
|
|
while selected_tool:
|
|
|
|
cv2.namedWindow("image")
|
|
|
|
selected_tool(selected_file)
|
|
|
|
cv2.destroyWindow("image")
|
2024-09-22 00:22:42 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2024-09-22 03:11:40 +00:00
|
|
|
tool_loop()
|