ssh-AI commited on
Commit
63d6aa2
·
verified ·
1 Parent(s): a3e5985

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -126
app.py CHANGED
@@ -1,134 +1,144 @@
1
  import streamlit as st
2
- from PIL import Image, ImageEnhance, ImageOps
 
 
3
  import numpy as np
4
- import io
5
  import zipfile
6
- import os
7
- import datetime
8
- import cv2
9
-
10
- # Set page configuration
11
- st.set_page_config(
12
- page_title="📄 Smart Doc Scanner",
13
- page_icon="🖼️",
14
- layout="centered",
15
- )
16
-
17
- # Custom CSS for background and buttons
18
- page_bg_img = '''
19
- <style>
20
- .stApp {
21
- background-image: url("https://images.unsplash.com/photo-1524995997946-a1c2e315a42f");
22
- background-size: cover;
23
- background-position: center;
24
- }
25
- h1 {
26
- color: #ffffff;
27
- text-align: center;
28
- text-shadow: 2px 2px #000000;
29
- }
30
- .stButton>button {
31
- color: white;
32
- background-color: #0078FF;
33
- border-radius: 10px;
34
- }
35
- </style>
36
- '''
37
-
38
- st.markdown(page_bg_img, unsafe_allow_html=True)
39
-
40
- st.markdown("<h1>📄 Smart Doc Scanner</h1>", unsafe_allow_html=True)
41
-
42
- # Upload multiple images
43
- uploaded_files = st.file_uploader(
44
- "Upload Document Images (JPEG, PNG, JPG)",
45
- type=["jpg", "jpeg", "png"],
46
- accept_multiple_files=True
47
- )
48
 
49
  if uploaded_files:
50
- enhance_option = st.checkbox("✨ Enhance Images (Scanner Effect)", value=True)
51
- manual_crop = st.checkbox("✂️ Manual Crop")
52
- auto_crop = st.checkbox("⚡ Auto Crop Background")
53
- rotate_left = st.button("⏪ Rotate Left")
54
- rotate_right = st.button("⏩ Rotate Right")
55
-
56
  processed_images = []
57
 
58
- for uploaded_file in uploaded_files:
59
- image = Image.open(uploaded_file).convert("RGB")
60
-
61
- # Rotate manually if needed
62
- if rotate_left:
63
- image = image.rotate(90, expand=True)
64
- if rotate_right:
65
- image = image.rotate(-90, expand=True)
66
-
67
- # Auto-crop (border removal)
68
- if auto_crop:
69
- open_cv_image = np.array(image)
70
- gray = cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2GRAY)
71
- _, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY_INV)
72
- contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
73
-
74
- if contours:
75
- x, y, w, h = cv2.boundingRect(contours[0])
76
- open_cv_image = open_cv_image[y:y+h, x:x+w]
77
- image = Image.fromarray(open_cv_image)
78
-
79
- # Manual crop if user wants
80
- if manual_crop:
81
- st.write("Adjust cropping manually below:")
82
- cropped_image = st.image(image, use_container_width=True)
83
- st.info("Manual cropping not yet interactive (advanced version can be added later).")
84
-
85
- # Enhance image
86
- if enhance_option:
87
- enhancer = ImageEnhance.Contrast(image)
88
- image = enhancer.enhance(1.5)
89
- sharpener = ImageEnhance.Sharpness(image)
90
- image = sharpener.enhance(2.0)
91
-
92
- # Display final image
93
- st.image(image, caption="Processed Image", use_container_width=True)
94
-
95
- processed_images.append(image)
96
-
97
- if processed_images:
98
- # Download processed images as zip
99
- zip_buffer = io.BytesIO()
100
- with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED) as zf:
101
- for idx, img in enumerate(processed_images):
102
- img_byte_arr = io.BytesIO()
103
- img.save(img_byte_arr, format='JPEG')
104
- zf.writestr(f"processed_image_{idx+1}.jpg", img_byte_arr.getvalue())
105
-
106
- st.download_button(
107
- label="📥 Download All Images (ZIP)",
108
- data=zip_buffer.getvalue(),
109
- file_name=f"processed_images_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.zip",
110
- mime="application/zip"
111
- )
112
-
113
- # Create PDF
114
- from fpdf import FPDF
115
- pdf = FPDF()
116
- for img in processed_images:
117
- img_path = "temp_img.jpg"
118
- img.save(img_path)
119
- width, height = img.size
120
- pdf.add_page()
121
- pdf.image(img_path, x=0, y=0, w=210, h=297)
122
- os.remove(img_path)
123
-
124
- pdf_output_path = f"scanned_document_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
125
- pdf.output(pdf_output_path)
126
-
127
- with open(pdf_output_path, "rb") as f:
128
- st.download_button(
129
- label="📄 Download as PDF",
130
- data=f,
131
- file_name=pdf_output_path,
132
- mime="application/pdf"
133
  )
134
- os.remove(pdf_output_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ from PIL import Image, ImageEnhance
3
+ from io import BytesIO
4
+ import os
5
  import numpy as np
 
6
  import zipfile
7
+ from fpdf import FPDF
8
+ from streamlit_cropper import st_cropper
9
+
10
+ # --- CONFIG ---
11
+ st.set_page_config(page_title="Smart Doc Scanner", page_icon="🖼️", layout="centered")
12
+
13
+ # --- BACKGROUND STYLE ---
14
+ def set_background():
15
+ page_bg_img = f"""
16
+ <style>
17
+ [data-testid="stAppViewContainer"] {{
18
+ background-image: url("https://images.unsplash.com/photo-1524995997946-a1c2e315a42f");
19
+ background-size: cover;
20
+ }}
21
+ [data-testid="stHeader"] {{
22
+ background: rgba(0, 0, 0, 0);
23
+ }}
24
+ </style>
25
+ """
26
+ st.markdown(page_bg_img, unsafe_allow_html=True)
27
+
28
+ set_background()
29
+
30
+ # --- LIGHT/DARK MODE TOGGLE ---
31
+ mode = st.selectbox("Select Theme:", ["Light Mode", "Dark Mode"])
32
+ if mode == "Dark Mode":
33
+ dark_style = """
34
+ <style>
35
+ body { background-color: #222; color: white; }
36
+ </style>
37
+ """
38
+ st.markdown(dark_style, unsafe_allow_html=True)
39
+
40
+ # --- APP TITLE ---
41
+ st.markdown("<h1 style='text-align: center; color: #2E86C1;'>📄 Smart ID & Document Scanner</h1>", unsafe_allow_html=True)
42
+ st.write("---")
43
+
44
+ # --- MODE SELECTION ---
45
+ app_mode = st.radio("Select Mode:", ["Normal Mode (No Editing)", "Advanced Mode (Edit Each Image)"])
46
+
47
+ # --- UPLOAD IMAGES ---
48
+ uploaded_files = st.file_uploader("Upload your Images", type=["png", "jpg", "jpeg"], accept_multiple_files=True)
49
 
50
  if uploaded_files:
51
+ st.write("---")
 
 
 
 
 
52
  processed_images = []
53
 
54
+ if app_mode == "Normal Mode (No Editing)":
55
+ for uploaded_file in uploaded_files:
56
+ img = Image.open(uploaded_file).convert("RGB")
57
+ processed_images.append(img)
58
+ st.image(img, caption=uploaded_file.name, use_container_width=True)
59
+
60
+ else: # Advanced Mode
61
+ st.info("🔧 Edit each image manually: crop, rotate, enhance, reset.")
62
+ for idx, uploaded_file in enumerate(uploaded_files):
63
+ original_img = Image.open(uploaded_file).convert("RGB")
64
+ if f"edited_{idx}" not in st.session_state:
65
+ st.session_state[f"edited_{idx}"] = original_img
66
+
67
+ st.subheader(f"Editing: {uploaded_file.name}")
68
+
69
+ # Cropper
70
+ cropped_img = st_cropper(
71
+ st.session_state[f"edited_{idx}"],
72
+ realtime_update=True,
73
+ box_color='#FF9900',
74
+ aspect_ratio=None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  )
76
+
77
+ # Actions
78
+ col1, col2, col3, col4 = st.columns(4)
79
+
80
+ with col1:
81
+ if st.button(f"Enhance {idx}", key=f"enhance_{idx}"):
82
+ enhancer = ImageEnhance.Contrast(cropped_img)
83
+ enhanced_img = enhancer.enhance(1.5)
84
+ sharpener = ImageEnhance.Sharpness(enhanced_img)
85
+ st.session_state[f"edited_{idx}"] = sharpener.enhance(2.0)
86
+
87
+ with col2:
88
+ angle = st.slider(f"Rotate {uploaded_file.name}", -180, 180, 0, key=f"rotate_{idx}")
89
+ if angle != 0:
90
+ st.session_state[f"edited_{idx}"] = cropped_img.rotate(angle, expand=True)
91
+
92
+ with col3:
93
+ if st.button(f"Reset {idx}", key=f"reset_{idx}"):
94
+ st.session_state[f"edited_{idx}"] = original_img
95
+ st.experimental_rerun()
96
+
97
+ with col4:
98
+ st.write("")
99
+
100
+ # Show Image
101
+ st.image(st.session_state[f"edited_{idx}"], caption=f"Edited {uploaded_file.name}", use_container_width=True)
102
+ processed_images.append(st.session_state[f"edited_{idx}"])
103
+
104
+ st.write("---")
105
+
106
+ # --- EXPORT OPTIONS ---
107
+ st.header("📥 Download Options")
108
+
109
+ col1, col2 = st.columns(2)
110
+
111
+ with col1:
112
+ if st.button("Download All as ZIP 📷"):
113
+ with st.spinner("Creating ZIP..."):
114
+ zip_buffer = BytesIO()
115
+ with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED) as zip_file:
116
+ for idx, img in enumerate(processed_images):
117
+ img_bytes = BytesIO()
118
+ img.save(img_bytes, format='PNG')
119
+ img_bytes.seek(0)
120
+ zip_file.writestr(f"image_{idx+1}.png", img_bytes.read())
121
+ zip_buffer.seek(0)
122
+ st.download_button(label="📥 Download ZIP", data=zip_buffer, file_name="images.zip", mime="application/zip")
123
+
124
+ with col2:
125
+ if st.button("Download All as PDF 📄"):
126
+ with st.spinner("Generating PDF..."):
127
+ pdf = FPDF()
128
+ temp_image_paths = []
129
+ for img in processed_images:
130
+ temp_path = f"/tmp/{np.random.randint(0, 999999)}.jpg"
131
+ img.save(temp_path, format="JPEG")
132
+ temp_image_paths.append(temp_path)
133
+ pdf.add_page()
134
+ pdf.image(temp_path, x=10, y=10, w=190)
135
+
136
+ pdf_output_path = "/tmp/converted.pdf"
137
+ pdf.output(pdf_output_path)
138
+
139
+ with open(pdf_output_path, "rb") as f:
140
+ st.download_button(label="📥 Download PDF", data=f, file_name="converted_document.pdf", mime="application/pdf")
141
+
142
+ for path in temp_image_paths:
143
+ os.remove(path)
144
+ os.remove(pdf_output_path)