Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| from io import BytesIO | |
| import barcode | |
| from barcode.writer import ImageWriter | |
| import qrcode | |
| import tempfile | |
| def image_to_bytes(img): | |
| pil_image = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) | |
| buffer = BytesIO() | |
| pil_image.save(buffer, format="PNG") | |
| return buffer.getvalue() | |
| def generate_barcode(link): | |
| code128 = barcode.get_barcode_class('code128') | |
| barcode_image = code128(link, writer=ImageWriter()) | |
| buffer = BytesIO() | |
| barcode_image.write(buffer) | |
| return Image.open(buffer) | |
| def generate_qrcode(link): | |
| qr = qrcode.QRCode( | |
| version=2, | |
| error_correction=qrcode.constants.ERROR_CORRECT_L, | |
| box_size=4, | |
| border=2, | |
| ) | |
| qr.add_data(link) | |
| qr.make(fit=True) | |
| qr_image = qr.make_image(fill_color="black", back_color="white") | |
| small_qr_image = qr_image.resize((512, 512), Image.Resampling.LANCZOS) | |
| buffer = BytesIO() | |
| small_qr_image.save(buffer, format="PNG") | |
| return Image.open(buffer) | |
| def add_custom_css(): | |
| css = """ | |
| <style> | |
| body { | |
| background: linear-gradient(135deg, #a8dadc, #f1faee); | |
| color: #1d3557; | |
| font-family: 'Arial', sans-serif; | |
| animation: backgroundAnimation 10s infinite alternate; | |
| } | |
| @keyframes backgroundAnimation { | |
| 0% { | |
| background: linear-gradient(135deg, #a8dadc, #f1faee); | |
| } | |
| 100% { | |
| background: linear-gradient(135deg, #f1faee, #457b9d); | |
| } | |
| } | |
| .stButton>button { | |
| background-color: #457b9d; | |
| color: white; | |
| border-radius: 5px; | |
| transition: transform 0.3s, background-color 0.3s; | |
| box-shadow: 2px 2px 6px rgba(0,0,0,0.2); | |
| } | |
| .stButton>button:hover { | |
| transform: scale(1.1); | |
| background-color: #1d3557; | |
| } | |
| .stSidebar { | |
| background: linear-gradient(to bottom, #87CEEB, #FFFFFF); | |
| color: white; | |
| font-size: 16px; | |
| } | |
| .stImage { | |
| animation: fadeIn 2s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| 0% { | |
| opacity: 0; | |
| } | |
| 100% { | |
| opacity: 1; | |
| } | |
| } | |
| header, footer { | |
| background: #457b9d; | |
| color: white; | |
| } | |
| .stMarkdown { | |
| animation: slideIn 1s ease-out; | |
| } | |
| @keyframes slideIn { | |
| 0% { | |
| transform: translateY(-20px); | |
| opacity: 0; | |
| } | |
| 100% { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| } | |
| </style> | |
| """ | |
| st.markdown(css, unsafe_allow_html=True) | |
| def add_custom_js(): | |
| js = """ | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const elements = document.querySelectorAll('.stButton>button'); | |
| elements.forEach(button => { | |
| button.addEventListener('click', () => { | |
| button.style.backgroundColor = '#a8dadc'; | |
| button.style.transform = 'rotate(360deg)'; | |
| setTimeout(() => button.style.transform = 'rotate(0deg)', 300); | |
| }); | |
| }); | |
| }); | |
| </script> | |
| """ | |
| st.markdown(js, unsafe_allow_html=True) | |
| def main(): | |
| st.set_page_config(page_title="ADS VISOR - Un autre regard", layout="wide") | |
| add_custom_css() | |
| add_custom_js() | |
| logo_path = "logo.jpg" | |
| logo = Image.open(logo_path) | |
| st.image(logo, width=150, caption="ADS VISOR") | |
| st.title("ADS VISOR - Un autre regard") | |
| st.sidebar.header("Chargement de l'image") | |
| if "default_image" not in st.session_state: | |
| st.session_state["default_image"] = None | |
| uploaded_file = st.sidebar.file_uploader("Charge une image", type=["png", "jpg", "jpeg"]) | |
| if uploaded_file is not None: | |
| image = Image.open(uploaded_file) | |
| st.session_state["default_image"] = np.array(image) | |
| st.sidebar.image(image, caption="Image par défaut", use_container_width=True) | |
| if st.session_state["default_image"] is None: | |
| st.sidebar.warning("Veuillez charger une image pour commencer.") | |
| return | |
| st.sidebar.header("Fonctionnalités") | |
| menu_option = st.sidebar.selectbox( | |
| "Choisissez une fonctionnalité", | |
| ["Accueil", "Niveaux de couleurs", "Cropping", "Rotation", "Floutage", "Contours", "Génération de Code-barres et QR Code", "Détection Faciale", "À propos de nous"], | |
| format_func=lambda x: { | |
| "Accueil": "🏠 Accueil", | |
| "Niveaux de couleurs": "🖼️ Niveaux de couleurs", | |
| "Cropping": "✂️ Cropping", | |
| "Rotation": "🔄 Rotation", | |
| "Floutage": "🌫️ Floutage", | |
| "Contours": "🔍 Contours", | |
| "Génération de Code-barres et QR Code": "📇 Codes numériques", | |
| "Détection Faciale": "🙂 Détection Faciale", | |
| "À propos de nous": "👨💻 À propos de nous" | |
| }.get(x, x) | |
| ) | |
| image_np = st.session_state["default_image"] | |
| if menu_option == "Accueil": | |
| st.header("Bienvenue sur ADS VISOR") | |
| st.markdown( | |
| """ | |
| <div class="stMarkdown"> | |
| <h2>ADS VISOR est une application innovante pour analyser, transformer et explorer vos images. 🖼️✨</h2> | |
| <p>Que vous soyez un professionnel ou un passionné, découvrez un large éventail de fonctionnalités interactives !</p> | |
| <ul> | |
| <li><b>Transformations d'image :</b> Couleurs, niveaux de gris, etc.</li> | |
| <li><b>Découpage & Rotation :</b> Ajustez vos images à la perfection.</li> | |
| <li><b>Détection Faciale :</b> Identifiez les visages automatiquement.</li> | |
| <li><b>Codes-barres & QR Codes :</b> Génération rapide pour vos projets.</li> | |
| </ul> | |
| </div> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| # Niveaux de couleurs | |
| elif menu_option == "Niveaux de couleurs": | |
| st.subheader("Niveaux de couleurs") | |
| # Instructions générales sur les niveaux de couleurs | |
| st.markdown(""" | |
| ### Légende : Fonctionnement des niveaux de couleurs | |
| - **Les niveaux de couleurs** permettent de manipuler les différentes composantes d'une image : **Rouge**, **Vert**, **Bleu**. | |
| - L'édition des canaux colorimétriques permet de visualiser une image en mettant en valeur une couleur spécifique. | |
| ### Options disponibles : | |
| - **Niveaux de Gris** : Convertit l'image en une seule nuance de gris, supprimant les informations de couleur. | |
| - **Rouge** : Filtre l'image pour ne conserver que la composante rouge. | |
| - **Vert** : Filtre l'image pour ne conserver que la composante verte. | |
| - **Jaune** : Combine les composantes rouge et verte pour donner une teinte jaune. | |
| ### Exemple : | |
| - Si vous choisissez l'option **Rouge**, vous obtiendrez une image où seules les nuances rouges sont visibles, les autres couleurs étant supprimées. | |
| """) | |
| # Création des onglets pour afficher les images dans différentes couleurs | |
| tab1, tab2, tab3, tab4 = st.tabs([ | |
| "Gris 🖤", "Rouge ❤️", "Vert 💚", "Jaune 💛" | |
| ]) | |
| # Onglet 1 : Gris | |
| with tab1: | |
| gray_image = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) | |
| st.image(gray_image, caption="Image en Niveaux de Gris", use_container_width=True) | |
| st.download_button( | |
| label="Télécharger l'image en gris", | |
| data=image_to_bytes(gray_image), | |
| file_name="image_gris.png", | |
| mime="image/png" | |
| ) | |
| # Onglet 2 : Rouge | |
| with tab2: | |
| red = image_np.copy() | |
| red[:, :, 1:] = 0 # On met les canaux vert et bleu à zéro | |
| st.image(red, caption="Image Rouge", use_container_width=True) | |
| st.download_button( | |
| label="Télécharger l'image rouge", | |
| data=image_to_bytes(red), | |
| file_name="image_rouge.png", | |
| mime="image/png" | |
| ) | |
| # Onglet 3 : Vert | |
| with tab3: | |
| green = image_np.copy() | |
| green[:, :, [0, 2]] = 0 # On met les canaux rouge et bleu à zéro | |
| st.image(green, caption="Image Verte", use_container_width=True) | |
| st.download_button( | |
| label="Télécharger l'image verte", | |
| data=image_to_bytes(green), | |
| file_name="image_verte.png", | |
| mime="image/png" | |
| ) | |
| # Onglet 4 : Jaune | |
| with tab4: | |
| yellow = image_np.copy() | |
| yellow[:, :, 0] = 0 # On supprime la composante bleue | |
| st.image(yellow, caption="Image Jaune", use_container_width=True) | |
| st.download_button( | |
| label="Télécharger l'image jaune", | |
| data=image_to_bytes(yellow), | |
| file_name="image_jaune.png", | |
| mime="image/png" | |
| ) | |
| elif menu_option == "Cropping": | |
| st.subheader("Cropping - Recadrage d'Image") | |
| # Explications pour les paramètres | |
| st.markdown( | |
| """ | |
| ### Légende : Fonctionnement du recadrage | |
| Le recadrage d'une image consiste à extraire une portion rectangulaire de celle-ci. Pour cela, nous définissons les coordonnées des coins du rectangle : | |
| - **`x1` (Coordonnée x du coin supérieur gauche)** : Position horizontale du début du rectangle (0 ≤ `x1` < largeur-1). | |
| - **`y1` (Coordonnée y du coin supérieur gauche)** : Position verticale du début du rectangle (0 ≤ `y1` < hauteur-1). | |
| - **`x2` (Coordonnée x du coin inférieur droit)** : Position horizontale de la fin du rectangle (1 ≤ `x2` ≤ largeur). | |
| - **`y2` (Coordonnée y du coin inférieur droit)** : Position verticale de la fin du rectangle (1 ≤ `y2` ≤ hauteur). | |
| ### Exemple illustratif : | |
| Imaginons que vous avez une image de 300x300 pixels. Si vous entrez `x1=50`, `y1=50`, `x2=200`, et `y2=200`, l'image sera recadrée en un rectangle de taille 150x150 pixels, commençant à partir du pixel (50, 50) jusqu'à (200, 200). | |
| ### Étapes : | |
| 1. Saisissez les coordonnées du rectangle que vous souhaitez extraire. | |
| 2. Vous verrez une prévisualisation de la zone sélectionnée avant de la confirmer. | |
| 3. Téléchargez l'image recadrée. | |
| """ | |
| ) | |
| # Entrée des coordonnées | |
| try: | |
| x1 = st.number_input("x1 (Coordonnée x du coin supérieur gauche)", 0, image_np.shape[1] - 1, step=1) | |
| y1 = st.number_input("y1 (Coordonnée y du coin supérieur gauche)", 0, image_np.shape[0] - 1, step=1) | |
| x2 = st.number_input("x2 (Coordonnée x du coin inférieur droit)", 1, image_np.shape[1], step=1) | |
| y2 = st.number_input("y2 (Coordonnée y du coin inférieur droit)", 1, image_np.shape[0], step=1) | |
| # Validation des coordonnées | |
| if x1 >= x2: | |
| st.error("`x2` doit être strictement supérieur à `x1`. Veuillez corriger vos entrées.") | |
| elif y1 >= y2: | |
| st.error("`y2` doit être strictement supérieur à `y1`. Veuillez corriger vos entrées.") | |
| else: | |
| # Calcul des dimensions du recadrage | |
| cropped_width = x2 - x1 | |
| cropped_height = y2 - y1 | |
| # Bouton pour générer l'image recadrée | |
| if st.button("Générer le Recadrage"): | |
| # Recadrage de l'image | |
| cropped = image_np[int(y1):int(y2), int(x1):int(x2)] | |
| # Affichage de l'image recadrée | |
| st.image(cropped, caption="Image Croppée", use_container_width=True) | |
| # Affichage des informations sur la taille du rectangle | |
| st.write(f"Le rectangle recadré mesure {cropped_width} pixels de large et {cropped_height} pixels de haut.") | |
| # Bouton de téléchargement pour l'image recadrée | |
| st.download_button( | |
| label="⬇️ Télécharger l'image croppée", | |
| data=image_to_bytes(cropped), | |
| file_name="image_cropped.png", | |
| mime="image/png" | |
| ) | |
| except Exception as e: | |
| st.error(f"Une erreur est survenue : {e}") | |
| # Rotation | |
| elif menu_option == "Rotation": | |
| st.subheader("Rotation") | |
| # Instructions sur la rotation | |
| st.markdown(""" | |
| ### Légende : Fonctionnement de la rotation | |
| - La **rotation** d'une image consiste à tourner l'image autour de son centre selon un angle défini. | |
| - Vous pouvez choisir un angle prédéfini pour faire pivoter l'image à gauche ou à droite. | |
| ### Options disponibles : | |
| - Choisissez un angle parmi les options proposées : **45°, 90° ou 180°**. | |
| - L'image sera ensuite tournée dans le sens des aiguilles d'une montre. | |
| ### Exemple : | |
| - Si vous choisissez un angle de **90°**, l'image sera pivotée de 90 degrés dans le sens des aiguilles d'une montre par rapport à son centre. | |
| """) | |
| # Sélection de l'angle de rotation | |
| angle = st.selectbox("Angle de rotation", [45, 90, 180]) | |
| # Calcul des dimensions et application de la rotation | |
| rows, cols, _ = image_np.shape | |
| rotation_matrix = cv2.getRotationMatrix2D((cols / 2, rows / 2), angle, 1) | |
| rotated = cv2.warpAffine(image_np, rotation_matrix, (cols, rows)) | |
| # Affichage de l'image après rotation | |
| st.image(rotated, caption=f"Image Rotée de {angle} degrés", use_container_width=True) | |
| # Bouton de téléchargement pour l'image rotée | |
| st.download_button( | |
| label="Télécharger l'image rotée", | |
| data=image_to_bytes(rotated), | |
| file_name=f"image_rotated_{angle}.png", | |
| mime="image/png" | |
| ) | |
| # Floutage | |
| elif menu_option == "Floutage": | |
| st.subheader("Floutage") | |
| # Instructions sur le floutage | |
| st.markdown(""" | |
| ### Légende : Fonctionnement du floutage | |
| - Le **floutage** est utilisé pour adoucir une image en réduisant les détails fins, ce qui est utile pour l'anonymisation ou pour appliquer des effets esthétiques. | |
| - Vous pouvez ajuster l'intensité du flou en fonction du niveau choisi. | |
| ### Options disponibles : | |
| - Le **Niveau de flou (k)** détermine l'intensité du flou appliqué. Plus la valeur est élevée, plus l'image sera floutée. | |
| - Les valeurs sont comprises entre 1 (très léger flou) et 51 (flou maximal), et doivent être des nombres impairs. | |
| ### Exemple : | |
| - Si vous choisissez **k = 15**, l'image sera floutée de manière modérée, et vous pourrez voir l'effet de réduction de détails. | |
| """) | |
| # Sélection du niveau de flou | |
| blur_level = st.slider("Niveau de flou (k)", min_value=1, max_value=51, step=2, value=15) | |
| # Application du flou sur l'image | |
| blurred = cv2.GaussianBlur(image_np, (blur_level, blur_level), 0) | |
| # Affichage de l'image floutée | |
| st.image(blurred, caption=f"Image Floutée (k={blur_level})", use_container_width=True) | |
| # Bouton de téléchargement pour l'image floutée | |
| st.download_button( | |
| label="Télécharger l'image floutée", | |
| data=image_to_bytes(blurred), | |
| file_name=f"image_blurred_k{blur_level}.png", | |
| mime="image/png" | |
| ) | |
| # Détection des contours | |
| elif menu_option == "Contours": | |
| st.subheader("Contours") | |
| # Instructions sur la détection des contours | |
| st.markdown(""" | |
| ### Légende : Fonctionnement de la détection de contours | |
| - La **détection de contours** consiste à identifier les bords distincts de l'image, ce qui permet de mieux comprendre les structures présentes. | |
| - Elle est souvent utilisée dans des traitements d'image avancés comme la reconnaissance d'objets ou l'analyse d'images. | |
| ### Options disponibles : | |
| - L'image sera convertie en niveaux de gris, et ensuite un algorithme de détection de contours (Canny) sera appliqué pour extraire les bords de l'image. | |
| - Cela permet de visualiser les contours dans des images complexes. | |
| ### Exemple : | |
| - Après l'application de l'algorithme Canny, l'image ne contient que les contours des objets, ce qui peut être utilisé pour détecter des formes, des objets ou d'autres détails visuels importants. | |
| """) | |
| # Conversion de l'image en niveaux de gris pour la détection des contours | |
| gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) | |
| edges = cv2.Canny(gray, 100, 200) | |
| # Affichage de l'image avec les contours détectés | |
| st.image(edges, caption="Contours de l'Image", use_container_width=True) | |
| # Bouton de téléchargement pour l'image avec contours | |
| st.download_button( | |
| label="Télécharger l'image avec contours", | |
| data=image_to_bytes(edges), | |
| file_name=f"image_edges.png", | |
| mime="image/png" | |
| ) | |
| elif menu_option == "Génération de Code-barres et QR Code": | |
| st.subheader("Génération de Code-barres et QR Code") | |
| # Légende pour guider l'utilisateur | |
| st.markdown( | |
| """ | |
| 🔄 **Instructions :** | |
| 1. Saisis un lien ou un texte dans le champ ci-dessous. | |
| 2. Accède à l'onglet souhaité pour générer un **Code-barres** 📊 ou un **QR Code** 📱. | |
| 3. Clique sur le bouton **"Générer"** pour voir le résultat et utilise le bouton **⬇️ Télécharger** pour sauvegarder l'image. | |
| """ | |
| ) | |
| # Gestion de l'entrée utilisateur avec sauvegarde dans session_state | |
| if "link" not in st.session_state: | |
| st.session_state["link"] = "" | |
| # Entrée utilisateur | |
| link = st.text_input("🔗 Entre un lien ou un texte pour générer les codes", value=st.session_state["link"]) | |
| if link: | |
| st.session_state["link"] = link | |
| # Création des onglets | |
| tab1, tab2 = st.tabs(["📊 Code-barres", "📱 QR Code"]) | |
| # Onglet Code-barres | |
| with tab1: | |
| st.header("📊 Génération de Code-barres") | |
| if st.button("Générer le Code-barres"): | |
| if st.session_state["link"]: | |
| try: | |
| # Génération du code-barres | |
| barcode_image = generate_barcode(st.session_state["link"]) | |
| st.image(barcode_image, caption="Code-barres généré", use_container_width=True) | |
| # Bouton de téléchargement avec icône | |
| barcode_buffer = BytesIO() | |
| barcode_image.save(barcode_buffer, format="PNG") | |
| barcode_buffer.seek(0) | |
| st.download_button( | |
| label="⬇️ Télécharger le Code-barres", | |
| data=barcode_buffer, | |
| file_name="barcode.png", | |
| mime="image/png" | |
| ) | |
| except Exception as e: | |
| st.error(f"Une erreur est survenue lors de la génération : {e}") | |
| else: | |
| st.warning("⚠️ Veuillez entrer un lien ou un texte valide.") | |
| # Onglet QR Code | |
| with tab2: | |
| st.header("📱 Génération de QR Code") | |
| if st.button("Générer le QR Code"): | |
| if st.session_state["link"]: | |
| try: | |
| # Génération du QR Code | |
| qrcode_image = generate_qrcode(st.session_state["link"]) | |
| st.image(qrcode_image, caption="QR Code généré", use_container_width=True) | |
| # Bouton de téléchargement avec icône | |
| qrcode_buffer = BytesIO() | |
| qrcode_image.save(qrcode_buffer, format="PNG") | |
| qrcode_buffer.seek(0) | |
| st.download_button( | |
| label="⬇️ Télécharger le QR Code", | |
| data=qrcode_buffer, | |
| file_name="qrcode.png", | |
| mime="image/png" | |
| ) | |
| except Exception as e: | |
| st.error(f"Une erreur est survenue lors de la génération : {e}") | |
| else: | |
| st.warning("⚠️ Veuillez entrer un lien ou un texte valide.") | |
| elif menu_option == "Détection Faciale": | |
| st.subheader("Détection Faciale") | |
| # Sélectionner la source de l'image | |
| source_option = st.radio("Choisissez la source", ("Image Importée", "Autre Image", "Webcam", "Vidéo")) | |
| # Charger le classificateur de visages pré-entrainé de OpenCV | |
| face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml") | |
| if source_option == "Image Importée": | |
| if st.session_state["default_image"] is not None: | |
| img = image_np | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) | |
| # Dessiner des rectangles autour des visages | |
| for (x, y, w, h) in faces: | |
| cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) | |
| st.image(img, caption="Image avec Visages Détectés", use_container_width=True) | |
| elif source_option == "Autre Image": | |
| uploaded_file_2 = st.file_uploader("Charge une autre image", type=["png", "jpg", "jpeg"]) | |
| if uploaded_file_2 is not None: | |
| image_2 = Image.open(uploaded_file_2) | |
| img = np.array(image_2) | |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) | |
| faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) | |
| # Dessiner des rectangles autour des visages | |
| for (x, y, w, h) in faces: | |
| cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2) | |
| st.image(img, caption="Autre Image avec Visages Détectés", use_container_width=True) | |
| elif source_option == "Webcam": | |
| stframe = st.empty() | |
| cap = cv2.VideoCapture(0) | |
| while True: | |
| ret, frame = cap.read() | |
| if not ret: | |
| st.write("Erreur dans la lecture de la webcam.") | |
| break | |
| gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
| faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) | |
| # Dessiner des rectangles autour des visages | |
| for (x, y, w, h) in faces: | |
| cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) | |
| # Affichage dans le streamlit | |
| stframe.image(frame, channels="BGR", use_container_width=True) | |
| if cv2.waitKey(1) & 0xFF == ord("q"): # Quitter avec la touche 'q' | |
| break | |
| cap.release() | |
| elif source_option == "Vidéo": | |
| video_file = st.file_uploader("Charge une vidéo", type=["mp4", "avi", "mov"]) | |
| if video_file is not None: | |
| video_bytes = video_file.read() | |
| with tempfile.NamedTemporaryFile(delete=False) as tmp_file: | |
| tmp_file.write(video_bytes) | |
| video_path = tmp_file.name | |
| cap = cv2.VideoCapture(video_path) | |
| while cap.isOpened(): | |
| ret, frame = cap.read() | |
| if not ret: | |
| break | |
| gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
| faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) | |
| # Dessiner des rectangles autour des visages | |
| for (x, y, w, h) in faces: | |
| cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2) | |
| st.image(frame, caption="Vidéo avec Visages Détectés", use_container_width=True) | |
| cap.release() | |
| elif menu_option == "À propos de nous": | |
| st.header("À propos de nous") | |
| st.markdown( | |
| """ | |
| <div style="text-align:center; font-family: Arial; margin: 20px 0;"> | |
| <h2>Notre équipe</h2> | |
| <p>Nous sommes une équipe passionnée travaillant sur des solutions innovantes.</p> | |
| </div> | |
| <div style="display: flex; justify-content: center; gap: 30px; flex-wrap: wrap;"> | |
| <div style="text-align: center;"> | |
| <img src="https://avatars.githubusercontent.com/u/1234567" alt="Ngoue David" style="width: 150px; height: 150px; border-radius: 50%;"> | |
| <h4>Ngoue David</h4> | |
| <p>🎓 Master 2 IA & Big Data</p> | |
| <p>📧 <a href="mailto:ngouedavidrogeryannick@gmail.com">ngouedavidrogeryannick@gmail.com</a></p> | |
| <p>🌐 <a href="https://github.com/TheBeyonder237" target="_blank">Profil GitHub</a></p> | |
| </div> | |
| <div style="text-align: center;"> | |
| <img src="https://avatars.githubusercontent.com/u/7654321" alt="Bidzanga Armel" style="width: 150px; height: 150px; border-radius: 50%;"> | |
| <h4>Bidzanga Armel</h4> | |
| <p>🎓 Master 2 IA & Big Data</p> | |
| <p>📧 <a href="mailto:armelmbia08@gmail.com">armelmbia08@gmail.com</a></p> | |
| <p>🌐 <a href="https://github.com/armelmbia10" target="_blank">Profil GitHub</a></p> | |
| </div> | |
| <div style="text-align: center;"> | |
| <img src="https://avatars.githubusercontent.com/u/9876543" alt="Nziou Serena" style="width: 150px; height: 150px; border-radius: 50%;"> | |
| <h4>Nziou Serena</h4> | |
| <p>🎓 Master 2 Administration Systèmes</p> | |
| <p>📧 <a href="mailto:serenakeliane@gmail.com">serenakeliane@gmail.com</a></p> | |
| <p>🌐 <a href="https://github.com/serkeli0011" target="_blank">Profil GitHub</a></p> | |
| </div> | |
| </div> | |
| """, | |
| unsafe_allow_html=True | |
| ) | |
| if __name__ == "__main__": | |
| main() | |