yiking/process.py
2024-12-16 21:39:20 +01:00

167 lines
5.5 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 = "___ ___"
import cv2
def capture_frame_from_webcam(cam_id):
"""
Captures a single frame from the webcam.
Returns:
frame (numpy.ndarray): The captured frame as a NumPy array.
"""
# Open a connection to the second webcam (index 0)
cap = cv2.VideoCapture(cam_id)
if not cap.isOpened():
raise Exception("Could not open webcam. Please check your webcam connection.")
try:
# Capture a single frame
ret, frame = cap.read()
if not ret:
raise Exception("Failed to capture frame from webcam.")
return frame
finally:
# Release the webcam resource
cap.release()
@dataclass
class Object:
x: int
y: int
rayon: int
def process_frame(params, cam_id):
"""
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_min"] * 4, params["color1_V_min"] * 4, params["color1_B_min"] * 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_min, color1_V_min, color1_B_min, color1_R_max, color1_V_max, color1_B_max) = itemgetter(
'minDist', 'param1', 'param2', 'minRadius', 'maxRadius',
'color1_R_min', 'color1_V_min', 'color1_B_min', 'color1_R_max', 'color1_V_max', 'color1_B_max'
)(params)
# 1. Acquisition de l'image
# src = dir_path.joinpath('tests/images/balls-full-small.jpg')
# raw_image = cv2.imread(str(src))
raw_image = capture_frame_from_webcam(cam_id)
# 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 = []
boules_bgr = []
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, v, r) = palette[np.argmax(counts)]
boules_bgr.append(f"R:{int(r)} V:{int(v)} B:{int(b)}")
# On récupère les valeurs de R G et B qui sont à analyser
if int(
color1_R_min <= math.floor(r) <= color1_R_max
and color1_V_min <= math.floor(v) <= color1_V_max
and color1_B_min <= math.floor(b) <= color1_B_max
):
boules_couleurs.append(TYPE_1)
else :
boules_couleurs.append(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_public = raw_image.copy()
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)
cv2.circle(img_public, (boule.x, boule.y), boule.rayon, (0, 255, 0), 2)
cv2.putText(img_final, boules_bgr[i], (boule.x, boule.y), cv2.FONT_HERSHEY_SIMPLEX,
fontScale=0.75, color=(255, 255, 255), thickness=1, lineType=cv2.LINE_AA)
cv2.imwrite('/tmp/yiking.png', img_public)
return img_final, return_text