SMART-61 rysowanie ruchem palca po canvasie i zapisywanie obrazka na dysk
This commit is contained in:
parent
bdf51e9f16
commit
8521680069
@ -35,13 +35,15 @@ class MyVideoCapture:
|
|||||||
self.mp_drawing.draw_landmarks(
|
self.mp_drawing.draw_landmarks(
|
||||||
image, hand_landmarks, self.mp_hands.HAND_CONNECTIONS)
|
image, hand_landmarks, self.mp_hands.HAND_CONNECTIONS)
|
||||||
# if cv2.waitKey(33) == ord('s'):
|
# if cv2.waitKey(33) == ord('s'):
|
||||||
fingers = {'index': (), 'middle': (), 'ring': (), 'pinky': ()}
|
# fingers = {'index': (), 'middle': (), 'ring': (), 'pinky': ()}
|
||||||
|
fingers = {}
|
||||||
if results.multi_hand_landmarks:
|
if results.multi_hand_landmarks:
|
||||||
for hand_landmarks in results.multi_hand_landmarks:
|
for hand_landmarks in results.multi_hand_landmarks:
|
||||||
fingers['index'] = sgest.check_index_finger(hand_landmarks)
|
fingers['index'] = sgest.check_index_finger(hand_landmarks)
|
||||||
fingers['middle'] = sgest.check_middle_finger(hand_landmarks)
|
fingers['start_stop'] = sgest.get_start_stop(hand_landmarks)
|
||||||
fingers['ring'] = sgest.check_ring_finger(hand_landmarks)
|
# fingers['middle'] = sgest.check_middle_finger(hand_landmarks)
|
||||||
fingers['pinky'] = sgest.check_pinky_finger(hand_landmarks)
|
# fingers['ring'] = sgest.check_ring_finger(hand_landmarks)
|
||||||
|
# fingers['pinky'] = sgest.check_pinky_finger(hand_landmarks)
|
||||||
else:
|
else:
|
||||||
fingers = None
|
fingers = None
|
||||||
return fingers, image, success
|
return fingers, image, success
|
||||||
|
@ -1,28 +1,62 @@
|
|||||||
from math import sqrt
|
from math import sqrt
|
||||||
|
|
||||||
|
INDEX_FINGER_TOP = 8
|
||||||
|
INDEX_FINGER_BOTTOM = 5
|
||||||
|
MIDDLE_FINGER_TOP = 12
|
||||||
|
MIDDLE_FINGER_BOTTOM = 9
|
||||||
|
RING_FINGER_TOP = 16
|
||||||
|
RING_FINGER_BOTTOM = 13
|
||||||
|
PINKY_FINGER_TOP = 20
|
||||||
|
PINKY_FINGER_BOTTOM = 17
|
||||||
|
THUMB_TOP = 4
|
||||||
|
THUMB_CENTER = 3
|
||||||
|
THUMB_BOTTOM = 2
|
||||||
|
|
||||||
|
|
||||||
def calculate_distance(ax, ay, bx, by):
|
def calculate_distance(ax, ay, bx, by):
|
||||||
distance = sqrt(((bx - ax) ** 2 + (by - ay) ** 2))
|
distance = sqrt(((bx - ax) ** 2 + (by - ay) ** 2))
|
||||||
return distance
|
return distance
|
||||||
|
|
||||||
|
|
||||||
|
def get_start_stop(hand_landmarks):
|
||||||
|
index_finger = check_index_finger(hand_landmarks)
|
||||||
|
middle_finger = check_middle_finger(hand_landmarks)
|
||||||
|
ring_finger = check_ring_finger(hand_landmarks)
|
||||||
|
pinky_finger = check_pinky_finger(hand_landmarks)
|
||||||
|
is_thumb_near_index = is_thumb_near_index_finger(hand_landmarks)
|
||||||
|
|
||||||
|
if not index_finger['straight'] and middle_finger['straight'] and ring_finger['straight'] and pinky_finger['straight'] and is_thumb_near_index:
|
||||||
|
return 'start'
|
||||||
|
else:
|
||||||
|
thumb_top_y = hand_landmarks.landmark[THUMB_TOP].y
|
||||||
|
thumb_bottom_y = hand_landmarks.landmark[THUMB_BOTTOM].y
|
||||||
|
is_thumb_straight = check_finger(hand_landmarks, THUMB_TOP, THUMB_BOTTOM)['straight']
|
||||||
|
if is_thumb_straight:
|
||||||
|
if thumb_bottom_y < thumb_top_y:
|
||||||
|
return 'stop'
|
||||||
|
else:
|
||||||
|
return 'nothing'
|
||||||
|
else:
|
||||||
|
return 'nothing'
|
||||||
|
|
||||||
|
|
||||||
def check_index_finger(hand_landmarks):
|
def check_index_finger(hand_landmarks):
|
||||||
return check_finger(hand_landmarks, 8, 5, 'wskazujacy')
|
return check_finger(hand_landmarks, INDEX_FINGER_TOP, INDEX_FINGER_BOTTOM)
|
||||||
|
|
||||||
|
|
||||||
def check_middle_finger(hand_landmarks):
|
def check_middle_finger(hand_landmarks):
|
||||||
return check_finger(hand_landmarks, 12, 9, 'srodkowy')
|
return check_finger(hand_landmarks, MIDDLE_FINGER_TOP, MIDDLE_FINGER_BOTTOM)
|
||||||
|
|
||||||
|
|
||||||
def check_ring_finger(hand_landmarks):
|
def check_ring_finger(hand_landmarks):
|
||||||
return check_finger(hand_landmarks, 16, 13, 'serdeczny')
|
return check_finger(hand_landmarks, RING_FINGER_TOP, RING_FINGER_BOTTOM)
|
||||||
|
|
||||||
|
|
||||||
def check_pinky_finger(hand_landmarks):
|
def check_pinky_finger(hand_landmarks):
|
||||||
return check_finger(hand_landmarks, 20, 17, 'maly')
|
return check_finger(hand_landmarks, PINKY_FINGER_TOP, PINKY_FINGER_BOTTOM)
|
||||||
|
|
||||||
|
|
||||||
def check_finger(hand_landmarks, top_idx, bottom_idx, name):
|
def check_finger(hand_landmarks, top_idx, bottom_idx):
|
||||||
ax = hand_landmarks.landmark[top_idx].x
|
ax = hand_landmarks.landmark[top_idx].x
|
||||||
ay = hand_landmarks.landmark[top_idx].y
|
ay = hand_landmarks.landmark[top_idx].y
|
||||||
bx = hand_landmarks.landmark[bottom_idx].x
|
bx = hand_landmarks.landmark[bottom_idx].x
|
||||||
@ -35,7 +69,14 @@ def check_finger(hand_landmarks, top_idx, bottom_idx, name):
|
|||||||
distance_bottom_start = calculate_distance(ax, ay, bx, by)
|
distance_bottom_start = calculate_distance(ax, ay, bx, by)
|
||||||
|
|
||||||
if distance_bottom_start < distance_top_bottom + 0.1:
|
if distance_bottom_start < distance_top_bottom + 0.1:
|
||||||
result = name + '_wyprostowany'
|
straight = True
|
||||||
else:
|
else:
|
||||||
result = name + '_niewyprostowany'
|
straight = False
|
||||||
return result, hand_landmarks.landmark[top_idx].x, hand_landmarks.landmark[top_idx].y
|
return {'straight': straight, 'x': hand_landmarks.landmark[top_idx].x, 'y': hand_landmarks.landmark[top_idx].y}
|
||||||
|
|
||||||
|
|
||||||
|
def is_thumb_near_index_finger(hand_landmarks):
|
||||||
|
thumb_top = hand_landmarks.landmark[THUMB_TOP]
|
||||||
|
index_finger_top = hand_landmarks.landmark[INDEX_FINGER_TOP]
|
||||||
|
distance = calculate_distance(thumb_top.x, thumb_top.y, index_finger_top.x, index_finger_top.y)
|
||||||
|
return distance < 0.1
|
||||||
|
BIN
client/image.png
Normal file
BIN
client/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -1,78 +0,0 @@
|
|||||||
import PIL.Image
|
|
||||||
import PIL.ImageTk
|
|
||||||
import cv2
|
|
||||||
import time
|
|
||||||
import tkinter
|
|
||||||
|
|
||||||
|
|
||||||
class App:
|
|
||||||
def __init__(self, window, window_title, video_source=0):
|
|
||||||
self.window = window
|
|
||||||
|
|
||||||
self.window.title(window_title)
|
|
||||||
self.video_source = video_source
|
|
||||||
|
|
||||||
# open video source (by default this will try to open the computer webcam)
|
|
||||||
self.vid = MyVideoCapture(self.video_source)
|
|
||||||
|
|
||||||
# Create a canvas that can fit the above video source size
|
|
||||||
self.canvas = tkinter.Canvas(window, width=self.vid.width, height=self.vid.height)
|
|
||||||
self.canvas.pack()
|
|
||||||
|
|
||||||
# Button that lets the user take a snapshot
|
|
||||||
self.btn_snapshot = tkinter.Button(window, text="Snapshot", width=50, command=self.snapshot)
|
|
||||||
self.btn_snapshot.pack(anchor=tkinter.CENTER, expand=True)
|
|
||||||
|
|
||||||
# After it is called once, the update method will be automatically called every delay milliseconds
|
|
||||||
self.delay = 15
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
self.window.mainloop()
|
|
||||||
|
|
||||||
def snapshot(self):
|
|
||||||
# Get a frame from the video source
|
|
||||||
ret, frame = self.vid.get_frame()
|
|
||||||
|
|
||||||
if ret:
|
|
||||||
cv2.imwrite("frame-" + time.strftime("%d-%m-%Y-%H-%M-%S") + ".jpg", cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
# Get a frame from the video source
|
|
||||||
ret, frame = self.vid.get_frame()
|
|
||||||
|
|
||||||
if ret:
|
|
||||||
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
|
|
||||||
self.canvas.create_image(0, 0, image=self.photo, anchor=tkinter.NW)
|
|
||||||
|
|
||||||
self.window.after(self.delay, self.update)
|
|
||||||
|
|
||||||
|
|
||||||
class MyVideoCapture:
|
|
||||||
def __init__(self, video_source=0):
|
|
||||||
# Open the video source
|
|
||||||
self.vid = cv2.VideoCapture(video_source)
|
|
||||||
if not self.vid.isOpened():
|
|
||||||
raise ValueError("Unable to open video source", video_source)
|
|
||||||
|
|
||||||
# Get video source width and height
|
|
||||||
self.width = self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)
|
|
||||||
self.height = self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
|
||||||
|
|
||||||
def get_frame(self):
|
|
||||||
if self.vid.isOpened():
|
|
||||||
ret, frame = self.vid.read()
|
|
||||||
if ret:
|
|
||||||
# Return a boolean success flag and the current frame converted to BGR
|
|
||||||
return (ret, cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
|
|
||||||
else:
|
|
||||||
return (ret, None)
|
|
||||||
else:
|
|
||||||
return (ret, None)
|
|
||||||
|
|
||||||
# Release the video source when the object is destroyed
|
|
||||||
def __del__(self):
|
|
||||||
if self.vid.isOpened():
|
|
||||||
self.vid.release()
|
|
||||||
|
|
||||||
|
|
||||||
App(tkinter.Tk(), "Tkinter and OpenCV")
|
|
@ -1,33 +0,0 @@
|
|||||||
import cv2
|
|
||||||
import tkinter as tk
|
|
||||||
from PIL import Image, ImageTk
|
|
||||||
|
|
||||||
|
|
||||||
class MainWindow():
|
|
||||||
def __init__(self, window, cap):
|
|
||||||
self.window = window
|
|
||||||
self.cap = cap
|
|
||||||
self.width = self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)
|
|
||||||
self.height = self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
|
||||||
self.interval = 20 # Interval in ms to get the latest frame
|
|
||||||
# Create canvas for image
|
|
||||||
self.canvas = tk.Canvas(self.window, width=self.width, height=self.height)
|
|
||||||
self.canvas.grid(row=0, column=0)
|
|
||||||
# Update image on canvas
|
|
||||||
self.update_image()
|
|
||||||
|
|
||||||
def update_image(self):
|
|
||||||
# Get the latest frame and convert image format
|
|
||||||
self.image = cv2.cvtColor(self.cap.read()[1], cv2.COLOR_BGR2RGB) # to RGB
|
|
||||||
self.image = Image.fromarray(self.image) # to PIL format
|
|
||||||
self.image = ImageTk.PhotoImage(self.image) # to ImageTk format
|
|
||||||
# Update image
|
|
||||||
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.image)
|
|
||||||
# Repeat every 'interval' ms
|
|
||||||
self.window.after(self.interval, self.update_image)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
root = tk.Tk()
|
|
||||||
MainWindow(root, cv2.VideoCapture(0))
|
|
||||||
root.mainloop()
|
|
@ -1,8 +1,9 @@
|
|||||||
import PIL.Image
|
import PIL.Image
|
||||||
import PIL.ImageTk
|
import PIL.ImageTk
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
import os
|
||||||
|
|
||||||
from constants import PROJECT_VIEW_NAME, FONT_SM, FONT, PROJECTS_VIEW_NAME
|
from constants import PROJECT_VIEW_NAME, FONT, PROJECTS_VIEW_NAME
|
||||||
from gestures.gesture_recognition import MyVideoCapture
|
from gestures.gesture_recognition import MyVideoCapture
|
||||||
from views.abstract_view import AbstractView
|
from views.abstract_view import AbstractView
|
||||||
|
|
||||||
@ -15,14 +16,15 @@ class ProjectView(tk.Frame, AbstractView):
|
|||||||
self.window = controller
|
self.window = controller
|
||||||
self.main_view_controller = main_view_controller
|
self.main_view_controller = main_view_controller
|
||||||
self.delay = 20
|
self.delay = 20
|
||||||
|
self.canvas_width = 800
|
||||||
|
self.canvas_height = 415
|
||||||
|
self.start_draw = False
|
||||||
self.vid = None
|
self.vid = None
|
||||||
self.vid_canvas = None
|
self.vid_canvas = None
|
||||||
self.canvas = None
|
self.canvas = None
|
||||||
self.index_label = None
|
|
||||||
self.middle_label = None
|
|
||||||
self.ring_label = None
|
|
||||||
self.pinky_label = None
|
|
||||||
self.back_button = None
|
self.back_button = None
|
||||||
|
self.clear_button = None
|
||||||
|
self.save_button = None
|
||||||
self.bottom_frame = None
|
self.bottom_frame = None
|
||||||
self.top_frame = None
|
self.top_frame = None
|
||||||
|
|
||||||
@ -32,35 +34,29 @@ class ProjectView(tk.Frame, AbstractView):
|
|||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.vid = MyVideoCapture()
|
self.vid = MyVideoCapture()
|
||||||
|
self.canvas_width = self.window.winfo_width()
|
||||||
|
self.canvas_height = self.window.winfo_height() - 185
|
||||||
self.top_frame = tk.Frame(self, relief=tk.RAISED, borderwidth=0)
|
self.top_frame = tk.Frame(self, relief=tk.RAISED, borderwidth=0)
|
||||||
self.top_frame.pack(fill=tk.BOTH, side=tk.TOP)
|
self.top_frame.pack(fill=tk.BOTH, side=tk.TOP)
|
||||||
self.top_frame.columnconfigure(0, weight=2)
|
self.canvas = tk.Canvas(self.top_frame, width=self.canvas_width, height=self.canvas_height, bg="white")
|
||||||
self.top_frame.columnconfigure(1, weight=1)
|
self.canvas.grid(row=0, column=0)
|
||||||
print(self.top_frame['width'])
|
|
||||||
print(self.top_frame['height'])
|
|
||||||
self.canvas = tk.Canvas(self.top_frame, width=465, height=415, bg="blue")
|
|
||||||
coord = 10, 50, 240, 210
|
|
||||||
self.canvas.create_arc(coord, start=0, extent=150, fill="red")
|
|
||||||
self.canvas.grid(row=0, column=0, sticky=tk.W, rowspan=4)
|
|
||||||
self.index_label = tk.Label(self.top_frame, font=FONT_SM)
|
|
||||||
self.index_label.grid(row=0, column=1)
|
|
||||||
self.middle_label = tk.Label(self.top_frame, font=FONT_SM)
|
|
||||||
self.middle_label.grid(row=1, column=1)
|
|
||||||
self.ring_label = tk.Label(self.top_frame, font=FONT_SM)
|
|
||||||
self.ring_label.grid(row=2, column=1)
|
|
||||||
self.pinky_label = tk.Label(self.top_frame, font=FONT_SM)
|
|
||||||
self.pinky_label.grid(row=3, column=1)
|
|
||||||
|
|
||||||
self.bottom_frame = tk.Frame(self, relief=tk.RAISED, borderwidth=1)
|
self.bottom_frame = tk.Frame(self, relief=tk.RAISED, borderwidth=1)
|
||||||
self.bottom_frame.pack(fill=tk.BOTH, side=tk.BOTTOM)
|
self.bottom_frame.pack(fill=tk.BOTH, side=tk.BOTTOM)
|
||||||
self.bottom_frame.columnconfigure(0, weight=1)
|
self.bottom_frame.columnconfigure(0, weight=1)
|
||||||
self.bottom_frame.columnconfigure(1, weight=1)
|
self.bottom_frame.columnconfigure(1, weight=1)
|
||||||
|
self.bottom_frame.columnconfigure(2, weight=15)
|
||||||
self.back_button = tk.Button(self.bottom_frame, text="Back", font=FONT,
|
self.back_button = tk.Button(self.bottom_frame, text="Back", font=FONT,
|
||||||
command=lambda: self.back_to_projects_view())
|
command=lambda: self.back_to_projects_view())
|
||||||
self.back_button.grid(row=0, column=0, sticky=tk.SW)
|
self.back_button.grid(row=0, column=0, sticky=tk.SW, pady=10, padx=5)
|
||||||
|
self.clear_button = tk.Button(self.bottom_frame, text="Clear", font=FONT,
|
||||||
|
command=lambda: self.clear_canvas())
|
||||||
|
self.clear_button.grid(row=0, column=1, sticky=tk.SW, pady=10, padx=5)
|
||||||
|
self.save_button = tk.Button(self.bottom_frame, text="Save", font=FONT,
|
||||||
|
command=lambda: self.save())
|
||||||
|
self.save_button.grid(row=0, column=2, sticky=tk.SW, pady=10, padx=5)
|
||||||
self.vid_canvas = tk.Canvas(self.bottom_frame, width=self.vid.width, height=self.vid.height)
|
self.vid_canvas = tk.Canvas(self.bottom_frame, width=self.vid.width, height=self.vid.height)
|
||||||
self.vid_canvas.grid(row=0, column=1, sticky=tk.E)
|
self.vid_canvas.grid(row=0, column=3, sticky=tk.E)
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
@ -69,11 +65,19 @@ class ProjectView(tk.Frame, AbstractView):
|
|||||||
if self.vid is not None:
|
if self.vid is not None:
|
||||||
fingers, frame, success = self.vid.get_frame()
|
fingers, frame, success = self.vid.get_frame()
|
||||||
if fingers is not None:
|
if fingers is not None:
|
||||||
self.index_label['text'] = fingers['index'][0]
|
index_finger = fingers['index']
|
||||||
self.middle_label['text'] = fingers['middle'][0]
|
start_stop = fingers['start_stop']
|
||||||
self.ring_label['text'] = fingers['ring'][0]
|
if start_stop == 'start':
|
||||||
self.pinky_label['text'] = fingers['pinky'][0]
|
print('start')
|
||||||
|
self.start_draw = True
|
||||||
|
elif start_stop == 'stop':
|
||||||
|
print('stop')
|
||||||
|
self.start_draw = False
|
||||||
|
if self.start_draw and index_finger['straight']:
|
||||||
|
x = index_finger['x']
|
||||||
|
y = index_finger['y']
|
||||||
|
x, y = self.scale_points(x, y)
|
||||||
|
self.canvas.create_line(x, y, x+1, y, x-1, y, tags='point', width=3)
|
||||||
if success:
|
if success:
|
||||||
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
|
self.photo = PIL.ImageTk.PhotoImage(image=PIL.Image.fromarray(frame))
|
||||||
self.vid_canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)
|
self.vid_canvas.create_image(0, 0, image=self.photo, anchor=tk.NW)
|
||||||
@ -85,14 +89,22 @@ class ProjectView(tk.Frame, AbstractView):
|
|||||||
self.vid.release()
|
self.vid.release()
|
||||||
self.destroy_widgets()
|
self.destroy_widgets()
|
||||||
|
|
||||||
|
def clear_canvas(self):
|
||||||
|
self.canvas.delete('all')
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
self.canvas.postscript(file='image.eps')
|
||||||
|
img = PIL.Image.open('image.eps')
|
||||||
|
img.save('image.png', 'png')
|
||||||
|
os.remove('image.eps')
|
||||||
|
|
||||||
def destroy_widgets(self):
|
def destroy_widgets(self):
|
||||||
self.vid = None
|
self.vid = None
|
||||||
self.top_frame.destroy()
|
self.top_frame.destroy()
|
||||||
self.canvas.destroy()
|
self.canvas.destroy()
|
||||||
self.bottom_frame.destroy()
|
self.bottom_frame.destroy()
|
||||||
self.vid_canvas.destroy()
|
self.vid_canvas.destroy()
|
||||||
self.index_label.destroy()
|
|
||||||
self.middle_label.destroy()
|
|
||||||
self.ring_label.destroy()
|
|
||||||
self.pinky_label.destroy()
|
|
||||||
self.back_button.destroy()
|
self.back_button.destroy()
|
||||||
|
|
||||||
|
def scale_points(self, x, y):
|
||||||
|
return x*self.canvas_width, y*self.canvas_height
|
||||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user