othmane107/ComputerVisionProject
0
1import streamlit as st2import cv23import numpy as np4from pyzbar.pyzbar import decode5from PIL import Image6from datetime import datetime7import io8 9# Configuration de la page - DOIT ÊTRE LA PREMIÈRE COMMANDE STREAMLIT10st.set_page_config(11 page_title="Scanner QR de Produits",12 page_icon="🛒",13 layout="wide",14 initial_sidebar_state="collapsed"15)16 17# Configuration pour éviter les erreurs CORS sur Hugging Face18st.markdown("""19<style>20 .stApp {21 max-width: 100%;22 }23 .uploadedFile {24 display: block !important;25 }26</style>27""", unsafe_allow_html=True)28 29# Initialisation du session state30if 'produits' not in st.session_state:31 st.session_state.produits = {}32if 'total' not in st.session_state:33 st.session_state.total = 0.034if 'derniere_image' not in st.session_state:35 st.session_state.derniere_image = None36if 'compteur_scan' not in st.session_state:37 st.session_state.compteur_scan = 038 39def ajouter_produit(nom, prix):40 """Ajoute un produit ou incrémente sa quantité"""41 if nom in st.session_state.produits:42 st.session_state.produits[nom]['quantite'] += 143 st.session_state.produits[nom]['total'] = (44 st.session_state.produits[nom]['quantite'] * prix45 )46 else:47 st.session_state.produits[nom] = {48 'prix_unitaire': prix,49 'quantite': 1,50 'total': prix51 }52 53 # Recalculer le total54 st.session_state.total = sum(55 p['total'] for p in st.session_state.produits.values()56 )57 58def supprimer_produit(nom):59 """Supprime un produit de la liste"""60 if nom in st.session_state.produits:61 del st.session_state.produits[nom]62 st.session_state.total = sum(63 p['total'] for p in st.session_state.produits.values()64 )65 66def modifier_quantite(nom, nouvelle_quantite):67 """Modifie la quantité d'un produit"""68 if nom in st.session_state.produits and nouvelle_quantite > 0:69 st.session_state.produits[nom]['quantite'] = nouvelle_quantite70 st.session_state.produits[nom]['total'] = (71 nouvelle_quantite * st.session_state.produits[nom]['prix_unitaire']72 )73 st.session_state.total = sum(74 p['total'] for p in st.session_state.produits.values()75 )76 elif nouvelle_quantite <= 0:77 supprimer_produit(nom)78 79def reinitialiser_caisse():80 """Réinitialise la caisse"""81 st.session_state.produits = {}82 st.session_state.total = 0.083 st.session_state.derniere_image = None84 85def scanner_qr_code(image):86 """Scanne les codes QR dans une image"""87 try:88 # Convertir PIL Image en array numpy89 img_array = np.array(image)90 91 # Convertir RGB en BGR pour OpenCV92 if len(img_array.shape) == 3 and img_array.shape[2] == 3:93 img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)94 elif len(img_array.shape) == 3 and img_array.shape[2] == 4:95 # Si RGBA, convertir en RGB d'abord96 img_array = cv2.cvtColor(img_array, cv2.COLOR_RGBA2RGB)97 img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)98 else:99 img_bgr = img_array100 101 # Décoder les codes QR102 codes = decode(img_bgr)103 104 resultats = []105 image_annotee = img_bgr.copy()106 107 for code in codes:108 try:109 data = code.data.decode('utf-8')110 except:111 data = str(code.data)112 113 # Dessiner le contour du QR code114 points = code.polygon115 if len(points) == 4:116 pts = np.array(points, np.int32)117 pts = pts.reshape((-1, 1, 2))118 cv2.polylines(image_annotee, [pts], True, (0, 255, 0), 3)119 120 # Afficher le texte121 x, y = points[0]122 cv2.putText(image_annotee, "QR Detecte", (x, y-10), 123 cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)124 125 # Parser les données126 if '|' in data:127 try:128 nom, prix_str = data.split('|', 1)129 prix = float(prix_str)130 resultats.append({131 'nom': nom.strip(),132 'prix': prix,133 'data_brute': data134 })135 except ValueError:136 resultats.append({137 'nom': None,138 'prix': None,139 'data_brute': data,140 'erreur': 'Format invalide - Prix doit être un nombre'141 })142 else:143 resultats.append({144 'nom': None,145 'prix': None,146 'data_brute': data,147 'erreur': 'Format attendu: NomProduit|Prix'148 })149 150 # Reconvertir BGR en RGB pour affichage151 image_annotee_rgb = cv2.cvtColor(image_annotee, cv2.COLOR_BGR2RGB)152 153 return resultats, image_annotee_rgb154 155 except Exception as e:156 st.error(f"Erreur lors du scan: {str(e)}")157 return [], np.array(image)158 159# Interface Streamlit160st.title("🛒 Scanner QR de Produits")161st.markdown("### 📸 Uploadez une image contenant un code QR")162 163# Colonnes principales164col1, col2 = st.columns([2, 1])165 166with col1:167 st.subheader("📤 Zone d'Upload")168 169 # Upload d'image avec une clé unique170 uploaded_file = st.file_uploader(171 "Choisissez une image (PNG, JPG, JPEG)",172 type=['png', 'jpg', 'jpeg'],173 help="Uploadez une image contenant un ou plusieurs codes QR au format: NomProduit|Prix",174 key=f"uploader_{st.session_state.compteur_scan}"175 )176 177 # Bouton de réinitialisation178 col_btn1, col_btn2 = st.columns(2)179 with col_btn1:180 if st.button("🔄 Réinitialiser la caisse", use_container_width=True):181 reinitialiser_caisse()182 st.session_state.compteur_scan += 1183 st.rerun()184 185 with col_btn2:186 if st.button("🗑️ Effacer l'image", use_container_width=True):187 st.session_state.compteur_scan += 1188 st.rerun()189 190 # Traitement de l'image uploadée191 if uploaded_file is not None:192 try:193 # Charger l'image194 image = Image.open(uploaded_file)195 196 # Convertir en RGB si nécessaire197 if image.mode != 'RGB':198 image = image.convert('RGB')199 200 # Scanner les codes QR201 with st.spinner("🔍 Scan en cours..."):202 resultats, image_annotee = scanner_qr_code(image)203 204 # Afficher l'image annotée205 st.image(image_annotee, caption="Image scannée", use_container_width=True)206 207 # Afficher les résultats208 if resultats:209 st.success(f"✅ {len(resultats)} code(s) QR détecté(s)")210 211 for i, resultat in enumerate(resultats, 1):212 if resultat['nom'] and resultat['prix'] is not None:213 col_res1, col_res2 = st.columns([3, 1])214 with col_res1:215 st.write(f"**{i}. {resultat['nom']}** - {resultat['prix']:.2f} DH")216 with col_res2:217 # Utiliser une clé unique pour chaque bouton218 btn_key = f"add_{st.session_state.compteur_scan}_{i}_{resultat['nom']}"219 if st.button(f"➕ Ajouter", key=btn_key):220 ajouter_produit(resultat['nom'], resultat['prix'])221 st.success(f"✅ {resultat['nom']} ajouté au panier!")222 st.rerun()223 elif 'erreur' in resultat:224 st.warning(f"⚠️ {i}. {resultat['erreur']}: `{resultat['data_brute']}`")225 else:226 st.info(f"ℹ️ {i}. Code détecté: `{resultat['data_brute']}`")227 else:228 st.warning("⚠️ Aucun code QR détecté dans l'image")229 st.info("💡 **Format attendu:** Le code QR doit contenir: `NomProduit|Prix`\n\nExemple: `Pain|5.50`")230 231 except Exception as e:232 st.error(f"❌ Erreur lors du traitement de l'image: {str(e)}")233 234 else:235 st.info("📷 Uploadez une image pour commencer le scan")236 237 # Instructions238 with st.expander("ℹ️ Instructions d'utilisation"):239 st.markdown("""240 **Comment utiliser le scanner:**241 242 1. 📸 Prenez une photo d'un code QR avec votre téléphone243 2. 📤 Uploadez l'image ci-dessus244 3. ✅ Le système détectera automatiquement les codes QR245 4. ➕ Cliquez sur "Ajouter" pour ajouter le produit au panier246 5. 🛒 Gérez votre panier dans la colonne de droite247 248 **Format du code QR:**249 ```250 NomProduit|Prix251 ```252 253 **Exemples:**254 - `Pain|5.50`255 - `Lait|8.00`256 - `Coca Cola|7.50`257 - `Pommes|12.50`258 259 **Astuces:**260 - Vous pouvez scanner plusieurs produits en une seule image261 - Assurez-vous que l'image est claire et bien éclairée262 - Le code QR doit être bien visible et pas trop petit263 """)264 265with col2:266 st.subheader("🧾 Panier")267 268 # Afficher les produits269 if st.session_state.produits:270 for nom, info in list(st.session_state.produits.items()):271 with st.container():272 st.markdown(f"**{nom}**")273 274 col_qty, col_prix, col_del = st.columns([2, 2, 1])275 276 with col_qty:277 # Utiliser une clé unique pour chaque input278 qty_key = f"qty_{nom}_{st.session_state.compteur_scan}"279 nouvelle_qty = st.number_input(280 "Qté",281 min_value=0,282 value=info['quantite'],283 key=qty_key,284 label_visibility="collapsed"285 )286 if nouvelle_qty != info['quantite']:287 modifier_quantite(nom, nouvelle_qty)288 st.rerun()289 290 with col_prix:291 st.write(f"{info['prix_unitaire']:.2f} DH × {info['quantite']}")292 st.write(f"**{info['total']:.2f} DH**")293 294 with col_del:295 del_key = f"del_{nom}_{st.session_state.compteur_scan}"296 if st.button("🗑️", key=del_key):297 supprimer_produit(nom)298 st.rerun()299 300 st.divider()301 302 # Total303 st.markdown("---")304 st.markdown(f"### 💰 TOTAL: {st.session_state.total:.2f} DH")305 306 # Bouton de paiement307 if st.button("💳 Finaliser le paiement", type="primary", use_container_width=True):308 st.success(f"✅ Paiement de {st.session_state.total:.2f} DH effectué!")309 st.balloons()310 reinitialiser_caisse()311 st.session_state.compteur_scan += 1312 st.rerun()313 else:314 st.info("🛒 Panier vide. Scannez des produits pour commencer.")315 316 # Statistiques du panier317 st.markdown("---")318 st.caption("📊 Statistiques:")319 st.caption(f"• Produits: 0")320 st.caption(f"• Total: 0.00 DH")321 322# Pied de page323st.markdown("---")324col_footer1, col_footer2 = st.columns(2)325with col_footer1:326 st.caption(f"🕒 Dernière mise à jour: {datetime.now().strftime('%H:%M:%S')}")327with col_footer2:328 st.caption("💡 Astuce: Vous pouvez scanner plusieurs produits en une seule image!")