Я занимаюсь разработкой веб-приложения, используя flask, аналогично виртуальной гардеробной, где пользователи могут примерить на них одежду. Я использую dlib для выполнения всех обнаружений ориентиров, чтобы найти положение для наложения моего изображения таким образом, чтобы было похоже, что человек носит эту ткань. Я использую Tkinter для обслуживания камеры и изображений в одном окне GUI, см. Это изображение ниже
Все отлично работает на локальном хосте, но при развертывании его на Heroku tkinter не работает, он перенаправляет сайт на локальный адрес хоста. Все логи c находятся в файле test.py, пожалуйста, посмотрите на этот файл ->
from tkinter_scroll import *
from tkinter import *
from PIL import Image
from PIL import ImageTk
import cv2, threading, os, time
from threading import Thread
from os import listdir
from os.path import isfile, join
import dlib
from imutils import face_utils, rotate_bound
import math
ACTIVE_IMAGES=[0 for i in range(100)]
def put_sprite(num, k):
global SPRITES, BTNS
SPRITES[num] = (1 - SPRITES[num])
if SPRITES[num]:
ACTIVE_IMAGES[num] = k
BTNS[num].config(relief=SUNKEN)
else:
BTNS[num].config(relief=RAISED)
def draw_sprite(frame, sprite, x_offset, y_offset):
(h,w) = (sprite.shape[0], sprite.shape[1])
(imgH,imgW) = (frame.shape[0], frame.shape[1])
if y_offset+h >= imgH:
sprite = sprite[0:imgH-y_offset,:,:]
if x_offset+w >= imgW:
sprite = sprite[:,0:imgW-x_offset,:]
if x_offset < 0:
sprite = sprite[:,abs(x_offset)::,:]
w = sprite.shape[1]
x_offset = 0
for c in range(3):
frame[y_offset:y_offset+h, x_offset:x_offset+w, c] = \
sprite[:,:,c] * (sprite[:,:,3]/255.0) + frame[y_offset:y_offset+h, x_offset:x_offset+w, c] * (1.0 - sprite[:,:,3]/255.0)
return frame
def adjust_sprite2head(sprite, head_width, head_ypos, ontop = True):
(h_sprite,w_sprite) = (sprite.shape[0], sprite.shape[1])
factor = 1.0*head_width/w_sprite
sprite = cv2.resize(sprite, (0,0), fx=factor, fy=factor)
(h_sprite,w_sprite) = (sprite.shape[0], sprite.shape[1])
y_orig = head_ypos-h_sprite if ontop else head_ypos
if (y_orig < 0):
sprite = sprite[abs(y_orig)::,:,:]
y_orig = 0
return (sprite, y_orig)
def apply_sprite2feature(image, sprite_path, haar_filter, x_offset, y_offset, y_offset_image, adjust2feature, desired_width, x, y, w, h):
sprite = cv2.imread(sprite_path,-1)
(h_sprite,w_sprite) = (sprite.shape[0], sprite.shape[1])
xpos = x + x_offset
ypos = y + y_offset
factor = 1.0*desired_width/w_sprite
sub_img = image[y + y_offset_image:y+h,x:x+w,:]
feature = apply_Haar_filter(sub_img, haar_filter, 1.3 , 10, 10)
if len(feature)!=0:
xpos, ypos = x, y + feature[0,1] #adjust only to feature in y axis (eyes)
if adjust2feature:
size_mustache = 1.2 #how many times bigger than mouth
factor = 1.0*(feature[0,2]*size_mustache)/w_sprite
xpos = x + feature[0,0] - int(feature[0,2]*(size_mustache-1)//2) #centered respect to width
ypos = y + y_offset_image + feature[0,1] - int(h_sprite*factor) #right on top
sprite = cv2.resize(sprite, (0,0), fx=factor, fy=factor)
image = draw_sprite(image,sprite,xpos,ypos)
def apply_sprite(image, path2sprite,w,x,y, angle, ontop = True):
sprite = cv2.imread(path2sprite,-1)
sprite = rotate_bound(sprite, angle)
(sprite, y_final) = adjust_sprite2head(sprite, w, y, ontop)
image = draw_sprite(image,sprite,x, y_final)
def calculate_inclination(point1, point2):
x1,x2,y1,y2 = point1[0], point2[0], point1[1], point2[1]
incl = 180/math.pi*math.atan((float(y2-y1))/(x2-x1))
return incl
def calculate_boundbox(list_coordinates):
x = min(list_coordinates[:,0])
y = min(list_coordinates[:,1])
w = max(list_coordinates[:,0]) - x
h = max(list_coordinates[:,1]) - y
return (x,y,w,h)
def apply_Haar_filter(img, haar_cascade,scaleFact = 1.05, minNeigh = 3, minSizeW = 30):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
features = haar_cascade.detectMultiScale(
gray,
scaleFactor=scaleFact,
minNeighbors=minNeigh,
minSize=(minSizeW, minSizeW),
flags=cv2.CASCADE_SCALE_IMAGE
)
return features
def get_face_boundbox(points, face_part):
if face_part == 1:
(x,y,w,h) = calculate_boundbox(points[17:22])
elif face_part == 2:
(x,y,w,h) = calculate_boundbox(points[22:27])
elif face_part == 3:
(x,y,w,h) = calculate_boundbox(points[36:42])
elif face_part == 4:
(x,y,w,h) = calculate_boundbox(points[42:48])
elif face_part == 5:
(x,y,w,h) = calculate_boundbox(points[29:36])
elif face_part == 6:
(x,y,w,h) = calculate_boundbox(points[1:17])
elif face_part == 7:
(x,y,w,h) = calculate_boundbox(points[0:6])
elif face_part == 8:
(x,y,w,h) = calculate_boundbox(points[11:17])
return (x,y,w,h)
def cvloop(run_event):
global ctr_mid
global SPRITES
i = 0
video_capture = cv2.VideoCapture(0)
video_capture.set(3,2048)
video_capture.set(4,2048)
(x,y,w,h) = (0,0,10,10)
detector = dlib.get_frontal_face_detector()
fullbody = cv2.CascadeClassifier('data/haarcascade_fullbody.xml')
model = "data/shape_predictor_68_face_landmarks.dat"
predictor = dlib.shape_predictor(model) # link to model: http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
while run_event.is_set():
ret, image = video_capture.read()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = detector(gray, 0)
for face in faces:
(x,y,w,h) = (face.left(), face.top(), face.width(), face.height())
shape = predictor(gray, face)
shape = face_utils.shape_to_np(shape)
incl = calculate_inclination(shape[17], shape[26])
is_mouth_open = (shape[66][1] -shape[62][1]) >= 10
if SPRITES[3]:#Tiara
apply_sprite(image, IMAGES[3][ACTIVE_IMAGES[3]],w+45,x-20,y+20, incl, ontop = True)
#Necklaces
if SPRITES[1]:
(x1,y1,w1,h1) = get_face_boundbox(shape, 6)
apply_sprite(image, IMAGES[1][ACTIVE_IMAGES[1]],w1,x1,y1+150, incl, ontop = False)
#Goggles
if SPRITES[6]:
(x3,y3,_,h3) = get_face_boundbox(shape, 1)
apply_sprite(image, IMAGES[6][ACTIVE_IMAGES[6]],w,x,y3-10, incl, ontop = False)
#Earrings
(x0,y0,w0,h0) = get_face_boundbox(shape, 6) #bound box of mouth
if SPRITES[2]:
(x3,y3,w3,h3) = get_face_boundbox(shape, 7) #nose
apply_sprite(image, IMAGES[2][ACTIVE_IMAGES[2]],w3,x3-40,y3+30, incl,ontop=False)
(x3,y3,w3,h3) = get_face_boundbox(shape, 8) #nose
apply_sprite(image, IMAGES[2][ACTIVE_IMAGES[2]],w3,x3+40,y3+75, incl)
# if SPRITES[5]:
# apply_sprite(image, IMAGES[5][ACTIVE_IMAGES[5]],w,x,y, incl, ontop = True)
#Frocks
if SPRITES[5]:
(x1,y1,w1,h1) = get_face_boundbox(shape, 8)
apply_sprite(image, IMAGES[5][ACTIVE_IMAGES[5]],w1+580,x1-350,y1+80, incl, ontop = False)
#Tops
if SPRITES[4]:
# (x,y,w,h) = (0,0,10,10)
# apply_sprite2feature(image, IMAGES[7][ACTIVE_IMAGES[7]], fullbody, w//4, 2*h//3, h//2, True, w//2, x, y, w, h)
(x1,y1,w1,h1) = get_face_boundbox(shape, 8)
apply_sprite(image, IMAGES[4][ACTIVE_IMAGES[4]],w1+350,x1-230,y1+100, incl, ontop = False)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image = Image.fromarray(image)
image = ImageTk.PhotoImage(image)
ctr_mid.configure(image=image)
ctr_mid.image = image
video_capture.release()
root = Tk()
root.title('TryOn')
app=FullScreenApp(root)
top_frame = Frame(root, bg='#077bd4', width=50, height=50, pady=3)
center = Frame(root, bg='white', width=50, height=40, padx=3, pady=3)
btm_frame = Frame(root, bg='#077bd4', width=50, height=50, pady=3)
root.grid_rowconfigure(1, weight=1)
root.grid_columnconfigure(0, weight=1)
top_frame.grid(row=0, sticky="ew")
center.grid(row=1, sticky="nsew")
btm_frame.grid(row=4, sticky="ew")
logo = ImageTk.PhotoImage(Image.open('logo.png').resize((120,60)))
model_label = Label(top_frame,image=logo)
model_label.grid(row=0, columnspan=3)
center.grid_rowconfigure(0, weight=1)
center.grid_columnconfigure(1, weight=1)
ctr_left = Label(center,bg='white', width=50, height=190)
ctr_mid = Label(center,bg='white',width=100, height=160, padx=0, pady=0)
ctr_left.grid(row=0, column=0, sticky="ns")
ctr_mid.grid(row=0, column=1, sticky="nsew")
scrollable_body = Scrollable(ctr_left, width=15)
SPRITES=[0 for i in range(10)]
BTNS=[]
IMAGES ={i:[] for i in range(10)}
PHOTOS ={i:[] for i in range(10)}
for img in sys.argv[1:]:
print(img.rsplit('/',1)[0][-1])
IMAGES[int(img.rsplit('/',1)[0][-1])].append(img)
image=ImageTk.PhotoImage(Image.open(img).resize((150,100)))
PHOTOS[int(img.rsplit('/',1)[0][-1])].append(image)
for index in range(9):
if len(PHOTOS[index]) > 0:
for k,photo in enumerate(PHOTOS[index]):
btn= Button(scrollable_body, highlightbackground='white', text=IMAGES[index][k].rsplit('/',1)[1].replace('.png','')[:-1],bg='white', image=photo, command = lambda index=index, k=k: put_sprite(index,k), compound=LEFT, width='300', height='200')
btn.pack(side="top", fill="both", expand="no", padx="5", pady="5")
BTNS.append(btn)
scrollable_body.update()
run_event = threading.Event()
run_event.set()
action = Thread(target=cvloop, args=(run_event,))
action.setDaemon(True)
action.start()
def terminate():
global root, run_event, action
run_event.clear()
time.sleep(1)
root.destroy()
root.protocol("WM_DELETE_WINDOW", terminate)
root.mainloop()
Я хочу решить эту проблему, если невозможно запустить tkinter на сервере, пожалуйста, скажите мне другой способ достижения этого типа функциональности.