Spaces:
Sleeping
Sleeping
File size: 5,865 Bytes
dbca1c6 c58b155 833b1ef dbca1c6 833b1ef dbca1c6 833b1ef dbca1c6 833b1ef dbca1c6 c58b155 dbca1c6 833b1ef dbca1c6 833b1ef c58b155 833b1ef 7752d57 36fea75 833b1ef 36fea75 833b1ef dbca1c6 833b1ef | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | import io
import os
import datetime
from typing import Tuple, List
import cv2
import numpy as np
from PIL import Image
import gradio as gr
CASCADE_PATH = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
face_cascade = cv2.CascadeClassifier(CASCADE_PATH)
# Optional Supabase client (configured via environment variables in Spaces)
supabase = None
try:
from supabase import create_client
SUPABASE_URL = os.environ.get('SUPABASE_URL')
SUPABASE_KEY = os.environ.get('SUPABASE_SERVICE_ROLE_KEY')
if SUPABASE_URL and SUPABASE_KEY:
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
except Exception:
supabase = None
def detect_faces_np(img: np.ndarray, scaleFactor: float = 1.1, minNeighbors: int = 5, minSize: Tuple[int, int] = (30, 30)) -> Tuple[Image.Image, List[Tuple[int, int, int, int]]]:
"""Detect faces in a numpy RGB image, return annotated PIL image and list of boxes (x,y,w,h)."""
# Ensure image is RGB numpy array
if img is None:
raise ValueError("No image provided")
# Convert to OpenCV BGR for drawing
if img.ndim == 2:
rgb = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
else:
# assume RGB
bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=scaleFactor, minNeighbors=minNeighbors, minSize=minSize)
# Draw rectangles
out_bgr = bgr.copy()
boxes = []
for (x, y, w, h) in faces:
cv2.rectangle(out_bgr, (x, y), (x + w, y + h), (0, 255, 0), 2)
boxes.append((int(x), int(y), int(w), int(h)))
# Convert back to RGB PIL Image
out_rgb = cv2.cvtColor(out_bgr, cv2.COLOR_BGR2RGB)
pil = Image.fromarray(out_rgb)
return pil, boxes
def detect_faces_gradio(image: Image.Image, scaleFactor: float = 1.1, minNeighbors: int = 5, minSize: int = 30):
"""Gradio wrapper: accepts PIL image and returns annotated image and textual summary."""
if image is None:
return None, "No image provided"
img_np = np.array(image.convert('RGB'))
pil_out, boxes = detect_faces_np(img_np, scaleFactor=scaleFactor, minNeighbors=int(minNeighbors), minSize=(int(minSize), int(minSize)))
if len(boxes) == 0:
return pil_out, "No faces detected"
summary_lines = [f"faces: {len(boxes)}"]
for i, (x, y, w, h) in enumerate(boxes, start=1):
summary_lines.append(f"{i}: x={x}, y={y}, w={w}, h={h}")
return pil_out, "\n".join(summary_lines)
def upload_image_to_supabase(pil_image: Image.Image, folder: str = 'captures'):
"""Upload a PIL Image to Supabase Storage and return public URL or None."""
if supabase is None:
return None
buf = io.BytesIO()
pil_image.save(buf, format='JPEG', quality=85)
buf.seek(0)
filename = f"{datetime.datetime.utcnow().strftime('%Y%m%d_%H%M%S_%f')}.jpg"
try:
supabase.storage.from_(folder).upload(path=filename, file=buf.getvalue(), file_options={"content-type": "image/jpeg"})
res = supabase.storage.from_(folder).get_public_url(filename)
if isinstance(res, dict):
return res.get('publicUrl') or res.get('public_url')
return str(res)
except Exception as e:
print('Supabase upload error:', e)
return None
def upload_annotated(image: Image.Image):
if image is None:
return "No image to upload"
url = upload_image_to_supabase(image)
if url:
return url
if supabase is None:
return "Supabase not configured (set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY)"
return "Upload failed"
def build_interface():
with gr.Blocks() as demo:
gr.Markdown("# Face Detection (OpenCV Haar Cascade)")
with gr.Row():
with gr.Column():
# Create Image input with webcam support when available; provide
# fallbacks for older Gradio versions that don't accept `source`.
try:
inp = gr.Image(source="webcam", type="pil", label="Webcam")
except TypeError:
try:
inp = gr.Image(type="pil", label="Webcam")
except Exception:
try:
inp = gr.inputs.Image(image_mode="RGB", label="Webcam")
except Exception:
inp = gr.Image(label="Webcam")
# Only provide a single Capture button for simplicity
run = gr.Button("Capture")
with gr.Column():
out_img = gr.Image(type="pil", label="Annotated image")
out_text = gr.Textbox(label="Detections")
# Camera returns either a numpy array or a PIL Image depending on Gradio version
def detect_wrapper(cam_image):
# Convert numpy array (BGR or RGB) to PIL Image consistently
img = cam_image
if isinstance(cam_image, (list, tuple)):
# Received as list (rare) -> convert to array
img = np.array(cam_image)
if isinstance(img, np.ndarray):
# If the array has 3 channels assume RGB from browser; convert to PIL
try:
pil = Image.fromarray(img.astype('uint8'), 'RGB')
except Exception:
# fallback: convert BGR->RGB
pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
else:
pil = img
return detect_faces_gradio(pil)
run.click(detect_wrapper, inputs=[inp], outputs=[out_img, out_text])
return demo
if __name__ == '__main__':
demo = build_interface()
demo.launch(server_name='0.0.0.0', server_port=int(__import__('os').environ.get('PORT', 7860))) |