feat: adds color detection
This commit is contained in:
parent
fa76a2f8fe
commit
10e8eecd31
61
README.md
61
README.md
@ -20,29 +20,58 @@ pip3 install -r requirements.txt
|
|||||||
python3 main.py
|
python3 main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
... devrait afficher ...
|
... devrait afficher une interface utilisateur.
|
||||||
|
|
||||||
```shell
|
## Documentation Utilisateur : Paramètres OpenCV
|
||||||
_________
|
|
||||||
_________
|
|
||||||
_________
|
|
||||||
___ ___
|
|
||||||
___ ___
|
|
||||||
___ ___
|
|
||||||
```
|
|
||||||
|
|
||||||
... qui signifie :
|
L'interface graphique **Yiking** permet de contrôler divers paramètres pour un traitement d'image réalisé avec OpenCV. Voici une explication de chaque paramètre et son rôle dans l'algorithme de détection.
|
||||||
|
|
||||||
> 11. LA PAIX (TAI 泰)
|
---
|
||||||
> La rencontre entre le Ciel et la Terre – une combinaison rare de grâce et de force -annonce la paix, l’harmonie, la stabilité et la prospérité. Un moment de consensus promettant des résultats constructifs et prometteurs. Ces circonstances augurent une coexistence pacifique entre tout le monde.
|
|
||||||
|
|
||||||
|
### Paramètres OpenCV
|
||||||
|
|
||||||
## Fonctionnement
|
#### 1. **maxDist**
|
||||||
|
- **Description** : Distance maximale entre les centres de deux cercles détectés.
|
||||||
|
- **Rôle** : Permet de contrôler la densité des cercles détectés. Une valeur élevée empêche la détection de cercles trop proches.
|
||||||
|
- **Conseil** : Augmentez cette valeur si trop de cercles se chevauchent.
|
||||||
|
|
||||||
|
#### 2. **param1**
|
||||||
|
- **Description** : Seuil supérieur pour le détecteur de bords (fonction Canny).
|
||||||
|
- **Rôle** : Définit la sensibilité de la détection des bords dans l'image. Une valeur élevée capture uniquement les bords bien définis.
|
||||||
|
- **Conseil** : Utilisez une valeur basse pour détecter plus de détails, mais avec plus de bruit.
|
||||||
|
|
||||||
|
#### 3. **param2**
|
||||||
|
- **Description** : Seuil pour le processus d'accumulation circulaire (HoughCircles).
|
||||||
|
- **Rôle** : Détermaxe la robustesse de la détection des cercles. Une valeur élevée garantit que seuls les cercles bien définis seront détectés.
|
||||||
|
- **Conseil** : Augmentez cette valeur pour réduire les faux positifs.
|
||||||
|
|
||||||
|
#### 4. **maxRadius**
|
||||||
|
- **Description** : Rayon maximal des cercles détectés.
|
||||||
|
- **Rôle** : Filtre les cercles dont le rayon est inférieur à cette valeur.
|
||||||
|
- **Conseil** : Utilisez une valeur basse si vous souhaitez détecter de petits cercles.
|
||||||
|
|
||||||
|
#### 5. **maxRadius**
|
||||||
|
- **Description** : Rayon maximal des cercles détectés.
|
||||||
|
- **Rôle** : Filtre les cercles dont le rayon dépasse cette valeur.
|
||||||
|
- **Conseil** : Réglez cette valeur en fonction de la taille des cercles que vous recherchez.
|
||||||
|
|
||||||
|
#### 6. **color1_R_max, color1_V_max, color1_B_max, color1_R_max, color1_V_max, color1_B_max,**
|
||||||
|
- **Description** : Couleur en (Rouge, Vert, Bleu) avec des valeurs minimales et maximales pour le filtrage par couleur.
|
||||||
|
- **Rôle** : Définit un seuil bas et un seuil haut pour détecter uniquement les pixels correspondants à une certaine teinte.
|
||||||
|
- **Conseil** : Ajustez ces valeurs pour isoler une couleur d'intérêt. Toute teinte en dehors de cette plage sera affectée à l'autre couleur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Utilisation
|
||||||
|
|
||||||
|
- Appuyer sur le bouton `Run` pour afficher une capture.
|
||||||
|
- Utiliser les sliders de détection de forme jusqu'à obtention du résultat voulu.
|
||||||
|
- Utiliser les valeurs (R, V, B) affichées pour les boules détectées pour définir la plage de couleur d'une couleur voulue.
|
||||||
|
|
||||||
|
## Exemple
|
||||||
|
|
||||||
### Source
|
### Source
|
||||||
![](./tests/images/balls-full-small.jpg)
|
![](./tests/images/balls-full-small.jpg)
|
||||||
|
|
||||||
### Post traitement
|
### Post traitement
|
||||||
![](./tests/images/balls-full-small-final.jpg)
|
![./tests/images/gui.png]()
|
||||||
|
|
||||||
|
|
67
main.py
67
main.py
@ -4,6 +4,58 @@ from process import process_frame
|
|||||||
from PIL import Image, ImageTk # Required for displaying images
|
from PIL import Image, ImageTk # Required for displaying images
|
||||||
import cv2 # OpenCV for numpy array to image conversion
|
import cv2 # OpenCV for numpy array to image conversion
|
||||||
|
|
||||||
|
"""
|
||||||
|
Here’s a detailed prompt you can use to generate the same GUI code anew using a language model:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Prompt:**
|
||||||
|
|
||||||
|
"I need a Python program to create a Tkinter-based GUI named 'Yiking'. The GUI will control parameters for an OpenCV image processing program. The program must be split into two files:
|
||||||
|
|
||||||
|
1. **`process.py`**: Contains a `process_frame` function that takes parameters as input, performs OpenCV operations (not implemented in this request), and returns an image as a NumPy array and a result string.
|
||||||
|
|
||||||
|
2. **`gui.py`**: Implements the GUI and integrates with `process.py`. Here are the detailed requirements:
|
||||||
|
|
||||||
|
### GUI Details:
|
||||||
|
- **Window Title**: 'Yiking'
|
||||||
|
- **Layout**:
|
||||||
|
- **Row 1**: Two columns
|
||||||
|
- Left column: Group of sliders for controlling parameters.
|
||||||
|
- Right column: A canvas for displaying a processed image (1024x768 pixels).
|
||||||
|
- **Row 2**: Two columns
|
||||||
|
- Left column: A "Run" button.
|
||||||
|
- Right column: A text box for displaying results or error messages.
|
||||||
|
- **Widgets**:
|
||||||
|
- Sliders with corresponding min, max, and default values:
|
||||||
|
- `minDist` = (0, 500, 100)
|
||||||
|
- `param1` = (0, 500, 30)
|
||||||
|
- `param2` = (0, 400, 25)
|
||||||
|
- `minRadius` = (0, 100, 5)
|
||||||
|
- `maxRadius` = (0, 1000, 1000)
|
||||||
|
- `color1_R_min` = (0, 64, 5)
|
||||||
|
- `color1_V_min` = (0, 64, 5)
|
||||||
|
- `color1_B_min` = (0, 64, 5)
|
||||||
|
- `color1_R_min` = (0, 64, 5)
|
||||||
|
- `color1_V_min` = (0, 64, 5)
|
||||||
|
- `color1_B_min` = (0, 64, 5)
|
||||||
|
- Sliders must snap to increments of 5 and synchronize with a text entry box.
|
||||||
|
- The image canvas must resize input images to fit within 1024x768 while maintaining aspect ratio.
|
||||||
|
- A "Run" button executes the `process_frame` function with the current slider values.
|
||||||
|
- If an exception occurs in `process_frame`, it should display the error in the result text box and clear the canvas.
|
||||||
|
|
||||||
|
### Key Features:
|
||||||
|
- Use the `Pillow` library (`PIL`) to handle image conversion and resizing.
|
||||||
|
- Catch exceptions in `run_process` to display error messages.
|
||||||
|
- Keep the GUI responsive and visually clean.
|
||||||
|
|
||||||
|
### Output:
|
||||||
|
Please provide a fully functional `gui.py` implementation meeting these requirements. Include imports, class structure, and the `__main__` block. Do not implement the OpenCV functionality within `process.py`—assume it exists for integration purposes."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This prompt is designed to give the LLM everything it needs to generate the desired `gui.py` code. Let me know if you'd like to adjust it further!
|
||||||
|
"""
|
||||||
class OpenCVInterface:
|
class OpenCVInterface:
|
||||||
def __init__(self, root):
|
def __init__(self, root):
|
||||||
self.root = root
|
self.root = root
|
||||||
@ -16,12 +68,12 @@ class OpenCVInterface:
|
|||||||
"param2": (0, 400, 25),
|
"param2": (0, 400, 25),
|
||||||
"minRadius": (0, 100, 5),
|
"minRadius": (0, 100, 5),
|
||||||
"maxRadius": (0, 1000, 1000),
|
"maxRadius": (0, 1000, 1000),
|
||||||
"color1_R": (0, 64, 5),
|
"color1_R_min": (0, 255, 0),
|
||||||
"color1_V": (0, 64, 5),
|
"color1_R_max": (0, 255, 0),
|
||||||
"color1_B": (0, 64, 5),
|
"color1_V_min": (0, 255, 0),
|
||||||
"color2_R": (0, 64, 5),
|
"color1_V_max": (0, 255, 0),
|
||||||
"color2_V": (0, 64, 5),
|
"color1_B_min": (0, 255, 0),
|
||||||
"color2_B": (0, 64, 5),
|
"color1_B_max": (0, 255, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.variables = {
|
self.variables = {
|
||||||
@ -66,7 +118,7 @@ class OpenCVInterface:
|
|||||||
|
|
||||||
def on_slide(value):
|
def on_slide(value):
|
||||||
# Round value to nearest multiple of 5
|
# Round value to nearest multiple of 5
|
||||||
rounded_value = round(float(value) / 5) * 5
|
rounded_value = round(float(value) / 2) * 2
|
||||||
variable.set(int(rounded_value)) # Update the variable with the rounded value
|
variable.set(int(rounded_value)) # Update the variable with the rounded value
|
||||||
|
|
||||||
# Slider
|
# Slider
|
||||||
@ -100,7 +152,6 @@ class OpenCVInterface:
|
|||||||
new_width = int(original_width * aspect_ratio)
|
new_width = int(original_width * aspect_ratio)
|
||||||
new_height = int(original_height * aspect_ratio)
|
new_height = int(original_height * aspect_ratio)
|
||||||
pil_image = pil_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
pil_image = pil_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
||||||
|
|
||||||
tk_image = ImageTk.PhotoImage(pil_image) # Convert PIL Image to Tkinter Image
|
tk_image = ImageTk.PhotoImage(pil_image) # Convert PIL Image to Tkinter Image
|
||||||
|
|
||||||
# Clear canvas and display the image
|
# Clear canvas and display the image
|
||||||
|
23
process.py
23
process.py
@ -35,16 +35,16 @@ def process_frame(params):
|
|||||||
# Simulate processing: for now, return a dummy image and text.
|
# Simulate processing: for now, return a dummy image and text.
|
||||||
# width, height = 400, 300
|
# width, height = 400, 300
|
||||||
# image = Image.new("RGB", (width, height),
|
# image = Image.new("RGB", (width, height),
|
||||||
# color=(params["color1_R"] * 4, params["color1_V"] * 4, params["color1_B"] * 4))
|
# color=(params["color1_R_min"] * 4, params["color1_V_min"] * 4, params["color1_B_min"] * 4))
|
||||||
# image_tk = ImageTk.PhotoImage(image)
|
# image_tk = ImageTk.PhotoImage(image)
|
||||||
#
|
#
|
||||||
# result_text = f"Processed image with params: {params}"
|
# result_text = f"Processed image with params: {params}"
|
||||||
# return image_tk, result_text
|
# return image_tk, result_text
|
||||||
|
|
||||||
(minDist, param1, param2, minRadius, maxRadius,
|
(minDist, param1, param2, minRadius, maxRadius,
|
||||||
color1_R, color1_V, color1_B, color2_R, color2_V, color2_B) = itemgetter(
|
color1_R_min, color1_V_min, color1_B_min, color1_R_max, color1_V_max, color1_B_max) = itemgetter(
|
||||||
'minDist', 'param1', 'param2', 'minRadius', 'maxRadius',
|
'minDist', 'param1', 'param2', 'minRadius', 'maxRadius',
|
||||||
'color1_R', 'color1_V', 'color1_B', 'color2_R', 'color2_V', 'color2_B'
|
'color1_R_min', 'color1_V_min', 'color1_B_min', 'color1_R_max', 'color1_V_max', 'color1_B_max'
|
||||||
)(params)
|
)(params)
|
||||||
|
|
||||||
# 1. Acquisition de l'image
|
# 1. Acquisition de l'image
|
||||||
@ -83,6 +83,7 @@ def process_frame(params):
|
|||||||
s = np.clip(s, 0, 255)
|
s = np.clip(s, 0, 255)
|
||||||
imghsv = cv2.merge([h, s, v])
|
imghsv = cv2.merge([h, s, v])
|
||||||
boules_couleurs = []
|
boules_couleurs = []
|
||||||
|
boules_bgr = []
|
||||||
for boule in boules:
|
for boule in boules:
|
||||||
half_diametre = int(boule.rayon / 2)
|
half_diametre = int(boule.rayon / 2)
|
||||||
crop = imghsv[
|
crop = imghsv[
|
||||||
@ -94,10 +95,18 @@ def process_frame(params):
|
|||||||
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 200, .1)
|
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)
|
_, labels, palette = cv2.kmeans(pixels, n_colors, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
|
||||||
_, counts = np.unique(labels, return_counts=True)
|
_, counts = np.unique(labels, return_counts=True)
|
||||||
(b, g, r) = palette[np.argmax(counts)] / 16
|
(b, v, r) = palette[np.argmax(counts)]
|
||||||
|
boules_bgr.append(f"R:{int(r)} V:{int(v)} B:{int(b)}")
|
||||||
|
|
||||||
# A modulariser
|
# On récupère les valeurs de R G et B qui sont à analyser
|
||||||
boules_couleurs.append(TYPE_1 if b > 4 else TYPE_2)
|
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
|
# 5. Calcul des distances entre chaque boule et le cochonnet selon le centre des boxs
|
||||||
boules_distance = {}
|
boules_distance = {}
|
||||||
@ -116,5 +125,7 @@ def process_frame(params):
|
|||||||
boule = boules[i]
|
boule = boules[i]
|
||||||
return_text += f"{boules_couleurs[i]}\n"
|
return_text += f"{boules_couleurs[i]}\n"
|
||||||
cv2.circle(img_final, (boule.x, boule.y), boule.rayon, (0, 255, 0), 2)
|
cv2.circle(img_final, (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)
|
||||||
|
|
||||||
return img_final, return_text
|
return img_final, return_text
|
BIN
tests/images/gui.png
Normal file
BIN
tests/images/gui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 354 KiB |
Loading…
Reference in New Issue
Block a user