120 lines
4.0 KiB
Python
120 lines
4.0 KiB
Python
from operator import itemgetter
|
|
|
|
from PIL import Image, ImageTk
|
|
|
|
import math
|
|
from dataclasses import dataclass
|
|
|
|
import numpy as np
|
|
import cv2
|
|
from pathlib import Path
|
|
|
|
dir_path = Path(".").absolute()
|
|
TYPE_1 = "_________"
|
|
TYPE_2 = "___ ___"
|
|
|
|
|
|
@dataclass
|
|
class Object:
|
|
x: int
|
|
y: int
|
|
rayon: int
|
|
|
|
|
|
def process_frame(params):
|
|
"""
|
|
Simulates OpenCV processing using parameters from the GUI.
|
|
|
|
Args:
|
|
params (dict): A dictionary of variable values passed from the GUI.
|
|
|
|
Returns:
|
|
ImageTk.PhotoImage: A Tkinter-compatible image.
|
|
str: A result text description.
|
|
"""
|
|
# Simulate processing: for now, return a dummy image and text.
|
|
# width, height = 400, 300
|
|
# image = Image.new("RGB", (width, height),
|
|
# color=(params["color1_R"] * 4, params["color1_V"] * 4, params["color1_B"] * 4))
|
|
# image_tk = ImageTk.PhotoImage(image)
|
|
#
|
|
# result_text = f"Processed image with params: {params}"
|
|
# return image_tk, result_text
|
|
|
|
(minDist, param1, param2, minRadius, maxRadius,
|
|
color1_R, color1_V, color1_B, color2_R, color2_V, color2_B) = itemgetter(
|
|
'minDist', 'param1', 'param2', 'minRadius', 'maxRadius',
|
|
'color1_R', 'color1_V', 'color1_B', 'color2_R', 'color2_V', 'color2_B'
|
|
)(params)
|
|
|
|
# 1. Acquisition de l'image
|
|
src = dir_path.joinpath('tests/images/balls-full-small.jpg')
|
|
raw_image = cv2.imread(str(src))
|
|
|
|
# 2. Boxing des objets via opencv
|
|
gray = cv2.cvtColor(raw_image, cv2.COLOR_BGR2GRAY)
|
|
blurred = cv2.medianBlur(gray, 25)
|
|
|
|
circles = cv2.HoughCircles(blurred, cv2.HOUGH_GRADIENT, 1, minDist, param1=param1, param2=param2,
|
|
minRadius=minRadius,
|
|
maxRadius=maxRadius)
|
|
|
|
min_rayon = 9999
|
|
cochonnet = None
|
|
boules = []
|
|
if circles is not None:
|
|
circles = np.uint16(np.around(circles))
|
|
for i in circles[0, :]:
|
|
boule = Object(x=int(i[0]), y=int(i[1]), rayon=int(i[2]))
|
|
|
|
# 3. Détection de la box la plus petite : cochonnet
|
|
if boule.rayon < min_rayon:
|
|
min_rayon = boule.rayon
|
|
if cochonnet is not None:
|
|
boules.append(cochonnet)
|
|
cochonnet = boule
|
|
else:
|
|
boules.append(boule)
|
|
|
|
# 4. Regroupement en liste de boules 1 ou 2 selon la couleur principale de chaque box restante
|
|
hsv = cv2.cvtColor(raw_image, cv2.COLOR_BGR2HSV)
|
|
(h, s, v) = cv2.split(hsv)
|
|
s = s * 2
|
|
s = np.clip(s, 0, 255)
|
|
imghsv = cv2.merge([h, s, v])
|
|
boules_couleurs = []
|
|
for boule in boules:
|
|
half_diametre = int(boule.rayon / 2)
|
|
crop = imghsv[
|
|
boule.y - half_diametre:boule.y + half_diametre,
|
|
boule.x - half_diametre:boule.x + half_diametre,
|
|
].copy()
|
|
pixels = np.float32(crop.reshape(-1, 3))
|
|
n_colors = 2
|
|
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
|
|
_, labels, palette = cv2.kmeans(pixels, n_colors, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
|
|
_, counts = np.unique(labels, return_counts=True)
|
|
(b, g, r) = palette[np.argmax(counts)] / 16
|
|
|
|
# A modulariser
|
|
boules_couleurs.append(TYPE_1 if b > 4 else TYPE_2)
|
|
|
|
# 5. Calcul des distances entre chaque boule et le cochonnet selon le centre des boxs
|
|
boules_distance = {}
|
|
for i, boule in enumerate(boules):
|
|
dist = int(math.sqrt(math.pow(cochonnet.x - boule.x, 2) + math.pow(cochonnet.y - boule.y, 2)))
|
|
boules_distance[i] = dist
|
|
boules_distance = dict(sorted(boules_distance.items(), key=lambda item: item[1]))
|
|
|
|
# 6. Liste ordonnée des 6 distances les plus faibles
|
|
boules_proches = [x for x in list(boules_distance)[0:6]]
|
|
|
|
# 7. Sortie des 6 couleurs en --- ou - -
|
|
return_text = ""
|
|
img_final = raw_image.copy()
|
|
for i in boules_proches:
|
|
boule = boules[i]
|
|
return_text += f"{boules_couleurs[i]}\n"
|
|
cv2.circle(img_final, (boule.x, boule.y), boule.rayon, (0, 255, 0), 2)
|
|
|
|
return img_final, return_text |