ssh-AI commited on
Commit
c7a0bf4
·
verified ·
1 Parent(s): 6924fee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -119
app.py CHANGED
@@ -5,7 +5,6 @@ import numpy as np
5
  import zipfile
6
  import os
7
  from fpdf import FPDF
8
- from streamlit_drawable_canvas import st_canvas
9
 
10
  # --- PAGE CONFIG ---
11
  st.set_page_config(page_title="Smart Doc Scanner", page_icon="🖼️", layout="centered")
@@ -18,37 +17,21 @@ def resize_image(img, max_height=500):
18
  img = img.resize((new_width, max_height), resample=Image.Resampling.LANCZOS)
19
  return img
20
 
21
- # --- Custom Cropper (pass PIL Image directly) ---
22
- def custom_cropper(image, key="canvas_crop"):
23
- st.markdown("🖍️ Draw a rectangle over the area you want to crop.")
24
-
25
- canvas_result = st_canvas(
26
- fill_color="rgba(255, 165, 0, 0.3)",
27
- stroke_width=2,
28
- stroke_color="#FFA500",
29
- background_image=image, # ✅ Pass PIL image directly (no np.array)
30
- update_streamlit=True,
31
- height=image.height,
32
- width=image.width,
33
- drawing_mode="rect",
34
- key=key
35
- )
36
-
37
- if canvas_result.json_data and len(canvas_result.json_data["objects"]) > 0:
38
- obj = canvas_result.json_data["objects"][-1]
39
- left = int(obj["left"])
40
- top = int(obj["top"])
41
- width = int(obj["width"])
42
- height = int(obj["height"])
43
- cropped = image.crop((left, top, left + width, top + height))
44
- return cropped
45
- return None
46
 
47
  # --- HEADER ---
48
  st.title("📄 Smart ID & Document Scanner")
49
  st.markdown("---")
50
 
51
- # --- Clear Session State on New Upload ---
52
  if "uploaded_files_hash" not in st.session_state:
53
  st.session_state.uploaded_files_hash = None
54
 
@@ -57,110 +40,80 @@ uploaded_files = st.file_uploader("📤 Upload your Images", type=["png", "jpg",
57
  if uploaded_files:
58
  uploaded_files_names = [file.name for file in uploaded_files]
59
  current_hash = hash(tuple(uploaded_files_names))
60
-
61
  if st.session_state.uploaded_files_hash != current_hash:
62
  st.session_state.clear()
63
  st.session_state.uploaded_files_hash = current_hash
64
 
65
- app_mode = st.radio("🛠️ Select Mode:", ["Normal Mode (No Editing)", "Advanced Mode (Edit Each Image)"])
66
 
67
- st.markdown("---")
68
  processed_images = []
69
 
70
- if app_mode == "Normal Mode (No Editing)":
71
- for uploaded_file in uploaded_files:
72
- img = Image.open(uploaded_file).convert("RGB")
73
- img = resize_image(img)
74
- processed_images.append(img)
75
- st.image(img, caption=uploaded_file.name, use_container_width=True)
76
-
77
- else: # Advanced Mode
78
- for idx, uploaded_file in enumerate(uploaded_files):
79
- st.subheader(f"🖼️ Editing: {uploaded_file.name}")
80
-
81
- original_img = Image.open(uploaded_file).convert("RGB")
82
- original_img = resize_image(original_img)
83
-
84
- if f"edited_{idx}" not in st.session_state:
85
- st.session_state[f"edited_{idx}"] = original_img
86
- st.session_state[f"cropper_{idx}"] = original_img.copy()
87
- st.session_state[f"preview_{idx}"] = None
88
-
89
- col_cropper, col_preview = st.columns([1, 1])
90
-
91
- with col_cropper:
92
- st.markdown("### ✂️ Select Crop Area (Draw Rectangle):")
93
- crop_output = custom_cropper(st.session_state[f"cropper_{idx}"], key=f"canvas_{idx}")
94
-
95
- if crop_output is not None and st.button("✂️ Crop Now", key=f"crop_{idx}"):
96
- st.session_state[f"preview_{idx}"] = crop_output
97
- st.success("✅ Cropped! Now Edit Preview.")
98
-
99
- with col_preview:
100
- st.markdown("### 🎯 Preview and Edit:")
101
- if st.session_state[f"preview_{idx}"] is not None:
102
- st.image(st.session_state[f"preview_{idx}"], caption=f"Preview: {uploaded_file.name}", use_container_width=True)
 
 
103
 
104
- if st.button("✨ Enhance Image", key=f"enhance_{idx}"):
105
- enhancer = ImageEnhance.Contrast(st.session_state[f"preview_{idx}"])
106
- sharpener = ImageEnhance.Sharpness(enhancer.enhance(1.5))
107
- st.session_state[f"preview_{idx}"] = sharpener.enhance(2.0)
108
-
109
- angle = st.slider("🌀 Rotate Angle", -180, 180, 0, key=f"rotate_slider_{idx}")
110
-
111
- if st.button("🔄 Rotate Image", key=f"rotate_{idx}"):
112
- rotated_img = st.session_state[f"preview_{idx}"].rotate(angle, expand=True)
113
- st.session_state[f"preview_{idx}"] = rotated_img
114
-
115
- if st.button("🗑️ Reset Preview", key=f"reset_preview_{idx}"):
116
- st.session_state[f"preview_{idx}"] = crop_output.copy()
117
-
118
- if st.button("✅ Apply Changes", key=f"apply_{idx}"):
119
- st.session_state[f"edited_{idx}"] = st.session_state[f"preview_{idx}"].copy()
120
- st.success("✅ Changes Applied!")
121
-
122
- else:
123
- st.info("✂️ Please draw and crop an area first.")
124
-
125
- processed_images.append(st.session_state.get(f"edited_{idx}", original_img))
126
- st.markdown("---")
127
-
128
- # --- EXPORT OPTIONS ---
129
  st.header("📥 Download Options")
130
 
131
  col1, col2 = st.columns(2)
132
 
133
  with col1:
134
- if st.button("📸 Download All as ZIP"):
135
- with st.spinner("Creating ZIP..."):
136
- zip_buffer = BytesIO()
137
- with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED) as zip_file:
138
- for idx, img in enumerate(processed_images):
139
- img_bytes = BytesIO()
140
- img.save(img_bytes, format='PNG')
141
- img_bytes.seek(0)
142
- zip_file.writestr(f"image_{idx+1}.png", img_bytes.read())
143
- zip_buffer.seek(0)
144
- st.download_button(label="📥 Download ZIP", data=zip_buffer, file_name="images.zip", mime="application/zip")
145
 
146
  with col2:
147
- if st.button("📄 Download All as PDF"):
148
- with st.spinner("Generating PDF..."):
149
- pdf = FPDF()
150
- temp_image_paths = []
151
- for img in processed_images:
152
- temp_path = f"/tmp/{np.random.randint(0, 999999)}.jpg"
153
- img.save(temp_path, format="JPEG")
154
- temp_image_paths.append(temp_path)
155
- pdf.add_page()
156
- pdf.image(temp_path, x=10, y=10, w=190)
157
-
158
- pdf_output_path = "/tmp/converted.pdf"
159
- pdf.output(pdf_output_path)
160
-
161
- with open(pdf_output_path, "rb") as f:
162
- st.download_button(label="📥 Download PDF", data=f, file_name="converted_document.pdf", mime="application/pdf")
163
-
164
- for path in temp_image_paths:
165
- os.remove(path)
166
- os.remove(pdf_output_path)
 
5
  import zipfile
6
  import os
7
  from fpdf import FPDF
 
8
 
9
  # --- PAGE CONFIG ---
10
  st.set_page_config(page_title="Smart Doc Scanner", page_icon="🖼️", layout="centered")
 
17
  img = img.resize((new_width, max_height), resample=Image.Resampling.LANCZOS)
18
  return img
19
 
20
+ # --- Safe Cropping Using Sliders ---
21
+ def crop_with_sliders(image, key_prefix):
22
+ w, h = image.size
23
+ st.markdown("### ✂️ Use sliders to crop the image")
24
+ left = st.slider("Crop from left", 0, w - 10, 0, key=f"{key_prefix}_left")
25
+ top = st.slider("Crop from top", 0, h - 10, 0, key=f"{key_prefix}_top")
26
+ right = st.slider("Crop from right", left + 10, w, w, key=f"{key_prefix}_right")
27
+ bottom = st.slider("Crop from bottom", top + 10, h, h, key=f"{key_prefix}_bottom")
28
+ return image.crop((left, top, right, bottom))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  # --- HEADER ---
31
  st.title("📄 Smart ID & Document Scanner")
32
  st.markdown("---")
33
 
34
+ # --- Upload Section ---
35
  if "uploaded_files_hash" not in st.session_state:
36
  st.session_state.uploaded_files_hash = None
37
 
 
40
  if uploaded_files:
41
  uploaded_files_names = [file.name for file in uploaded_files]
42
  current_hash = hash(tuple(uploaded_files_names))
 
43
  if st.session_state.uploaded_files_hash != current_hash:
44
  st.session_state.clear()
45
  st.session_state.uploaded_files_hash = current_hash
46
 
47
+ mode = st.radio("🛠️ Select Mode", ["Normal Mode", "Advanced Mode (Crop/Edit)"])
48
 
 
49
  processed_images = []
50
 
51
+ for idx, file in enumerate(uploaded_files):
52
+ st.subheader(f"🖼️ {file.name}")
53
+ original = Image.open(file).convert("RGB")
54
+ resized = resize_image(original)
55
+
56
+ if mode == "Normal Mode":
57
+ st.image(resized, use_container_width=True)
58
+ processed_images.append(resized)
59
+ continue
60
+
61
+ # Advanced Mode
62
+ with st.expander("✂️ Crop & Edit Options", expanded=True):
63
+ cropped = crop_with_sliders(resized, key_prefix=f"img_{idx}")
64
+ st.image(cropped, caption="Cropped Preview", use_container_width=True)
65
+
66
+ col1, col2 = st.columns(2)
67
+ with col1:
68
+ if st.button("✨ Enhance", key=f"enhance_{idx}"):
69
+ enhancer = ImageEnhance.Contrast(cropped)
70
+ cropped = enhancer.enhance(1.5)
71
+ sharp = ImageEnhance.Sharpness(cropped).enhance(2.0)
72
+ cropped = sharp
73
+ st.success("Enhanced!")
74
+
75
+ with col2:
76
+ angle = st.slider("🌀 Rotate", -180, 180, 0, key=f"rotate_{idx}")
77
+ if angle != 0:
78
+ cropped = cropped.rotate(angle, expand=True)
79
+
80
+ if st.button("✅ Use This Image", key=f"finalize_{idx}"):
81
+ st.session_state[f"final_image_{idx}"] = cropped
82
+ st.success("Saved to export list!")
83
+
84
+ final = st.session_state.get(f"final_image_{idx}", resized)
85
+ processed_images.append(final)
86
 
87
+ st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  st.header("📥 Download Options")
89
 
90
  col1, col2 = st.columns(2)
91
 
92
  with col1:
93
+ if st.button("📸 Download ZIP"):
94
+ zip_buffer = BytesIO()
95
+ with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED) as zipf:
96
+ for i, img in enumerate(processed_images):
97
+ img_bytes = BytesIO()
98
+ img.save(img_bytes, format="PNG")
99
+ img_bytes.seek(0)
100
+ zipf.writestr(f"image_{i+1}.png", img_bytes.read())
101
+ zip_buffer.seek(0)
102
+ st.download_button("📥 Download ZIP", zip_buffer, "images.zip", "application/zip")
 
103
 
104
  with col2:
105
+ if st.button("📄 Download PDF"):
106
+ pdf = FPDF()
107
+ tmp_files = []
108
+ for img in processed_images:
109
+ path = f"/tmp/{np.random.randint(0, 1e6)}.jpg"
110
+ img.save(path, "JPEG")
111
+ pdf.add_page()
112
+ pdf.image(path, x=10, y=10, w=190)
113
+ tmp_files.append(path)
114
+ output = "/tmp/output.pdf"
115
+ pdf.output(output)
116
+ with open(output, "rb") as f:
117
+ st.download_button("📥 Download PDF", f, "scanned.pdf", "application/pdf")
118
+ for f in tmp_files + [output]:
119
+ os.remove(f)