Ahmed-El-Sharkawy commited on
Commit
cdde84b
·
verified ·
1 Parent(s): 95e31de

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +126 -0
  2. download.jpeg +0 -0
  3. requirements.txt +4 -0
app.py ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import io
3
+ import os
4
+ from dotenv import load_dotenv
5
+ from PIL import Image
6
+ import gradio as gr
7
+ from openai import OpenAI
8
+ import re
9
+
10
+ # Config
11
+ load_dotenv()
12
+ APP_Name = os.getenv("APP_Name", "Ghaymah Vision QA")
13
+ APP_Version = os.getenv("APP_Version", "1.0.0")
14
+ API_KEY = os.getenv("API_KEY")
15
+ BASE_URL = os.getenv("BASE_URL", "disappear")
16
+
17
+ CSS = """
18
+ .app-header{display:flex;align-items:center;gap:12px;justify-content:center;margin:6px 0 16px}
19
+ .app-header img{height:60px;border-radius:12px}
20
+ .app-title{font-weight:800;font-size:28px;line-height:1.1}
21
+ .app-sub{opacity:.7;font-size:14px}
22
+ """
23
+
24
+ # Branding
25
+ COMPANY_LOGO = "download.jpeg"
26
+ OWNER_NAME = "ENG. Ahmed Yasser El Sharkawy"
27
+
28
+ client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
29
+
30
+ # Map PIL formats to MIME types
31
+ PIL_TO_MIME = {
32
+ "JPEG": "image/jpeg",
33
+ "PNG": "image/png",
34
+ "WEBP": "image/webp",
35
+ "GIF": "image/gif",
36
+ "BMP": "image/bmp",
37
+ "TIFF": "image/tiff",
38
+ }
39
+
40
+ def encode_image_to_data_url(pil_image: Image.Image) -> str:
41
+ fmt = (pil_image.format or "PNG").upper()
42
+ mime = PIL_TO_MIME.get(fmt, "image/png")
43
+ buf = io.BytesIO()
44
+ if fmt == "JPEG" and pil_image.mode not in ("RGB", "L"):
45
+ pil_image = pil_image.convert("RGB")
46
+ pil_image.save(buf, format=fmt)
47
+ b64 = base64.b64encode(buf.getvalue()).decode("utf-8")
48
+ return f"data:{mime};base64,{b64}"
49
+
50
+ def logo_data_uri(path: str) -> str:
51
+ if not os.path.exists(path):
52
+ return ""
53
+ ext = os.path.splitext(path)[1].lower()
54
+ mime = {
55
+ ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg",
56
+ ".webp": "image/webp", ".gif": "image/gif"
57
+ }.get(ext, "image/png")
58
+ with open(path, "rb") as f:
59
+ b64 = base64.b64encode(f.read()).decode("utf-8")
60
+ return f"data:{mime};base64,{b64}"
61
+
62
+
63
+ def to_plain_text(s: str) -> str:
64
+ s = re.sub(r'\*\*(.*?)\*\*', r'\1', s) # bold
65
+ s = re.sub(r'\*(.*?)\*', r'\1', s) # italics
66
+ s = re.sub(r'`{1,3}(.*?)`{1,3}', r'\1', s, flags=re.S) # code
67
+ s = re.sub(r'^\s*[-*]\s+', '• ', s, flags=re.M) # bullets
68
+ return s
69
+
70
+ def ask_image_question(image: Image.Image, question: str):
71
+ if image is None:
72
+ return "⚠️ Please upload an image first."
73
+ if not question or not question.strip():
74
+ question = "Describe this image."
75
+ try:
76
+ data_url = encode_image_to_data_url(image)
77
+ msg_content = [
78
+ {"type": "text", "text": question.strip()},
79
+ {"type": "image_url", "image_url": {"url": data_url}},
80
+ ]
81
+ resp = client.chat.completions.create(
82
+ model="gemma-3-4b-it",
83
+ messages=[{"role": "user", "content": msg_content}],
84
+ max_tokens=400,
85
+ temperature=0.2,
86
+ )
87
+ return to_plain_text(resp.choices[0].message.content or "")
88
+
89
+ except Exception as e:
90
+ return f"❌ Error: {e}"
91
+
92
+ # Gradio UI
93
+ with gr.Blocks(title=f"{APP_Name} v{APP_Version}", css=CSS) as demo:
94
+ header_logo_src = logo_data_uri(COMPANY_LOGO)
95
+ logo_html = f"<img src='{header_logo_src}' alt='logo'>" if header_logo_src else ""
96
+ gr.HTML(f"""
97
+ <div class="app-header">
98
+ {logo_html}
99
+ <div class="app-header-text">
100
+ <div class="app-title">{APP_Name}</div>
101
+ <div class="app-sub">v{APP_Version} • {OWNER_NAME}</div>
102
+ </div>
103
+ </div>
104
+ """)
105
+
106
+ with gr.Row():
107
+ # Left column: image -> question -> ask button
108
+ with gr.Column(scale=3):
109
+ image_in = gr.Image(type="pil", label="Upload image", sources=["upload", "clipboard"])
110
+ question_in = gr.Textbox(label="Your question",
111
+ placeholder="e.g., What objects do you see? What is happening?",
112
+ lines=3)
113
+ ask_btn = gr.Button("Ask", variant="primary")
114
+
115
+ # Right column: logo -> answer box
116
+ with gr.Column(scale=2, min_width=320):
117
+ if os.path.exists(COMPANY_LOGO):
118
+ gr.Image(COMPANY_LOGO, show_label=False, container=False, height=96)
119
+ answer_out = gr.Textbox(label="Answer", lines=14, interactive=False, show_copy_button=True)
120
+
121
+ ask_btn.click(ask_image_question, [image_in, question_in], [answer_out])
122
+ question_in.submit(ask_image_question, [image_in, question_in], [answer_out])
123
+
124
+
125
+ if __name__ == "__main__":
126
+ demo.launch(debug=True)
download.jpeg ADDED
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ openai
3
+ python-dotenv
4
+ pillow