ssh-AI commited on
Commit
1a5f841
·
verified ·
1 Parent(s): 6a375fe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +129 -140
app.py CHANGED
@@ -1,169 +1,158 @@
1
- # app.py
2
-
3
  import streamlit as st
4
- import cv2
5
- import numpy as np
6
  from PIL import Image, ImageEnhance
 
7
  import io
 
8
  from fpdf import FPDF
9
- import datetime
10
-
11
- # -----------------------
12
- # Helper Functions
13
- # -----------------------
14
-
15
- def auto_crop_document(image):
16
- """Auto-detect document and crop it."""
17
- img = np.array(image)
18
- gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
19
- blurred = cv2.GaussianBlur(gray, (5, 5), 0)
20
- edged = cv2.Canny(blurred, 75, 200)
21
 
22
- contours, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
23
- contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
24
 
25
- for c in contours:
26
- peri = cv2.arcLength(c, True)
27
- approx = cv2.approxPolyDP(c, 0.02 * peri, True)
28
-
29
- if len(approx) == 4:
30
- doc_cnt = approx
31
- break
32
- else:
33
- return image # Return original if no contour found
34
-
35
- pts = doc_cnt.reshape(4, 2)
36
- rect = np.zeros((4, 2), dtype="float32")
37
-
38
- s = pts.sum(axis=1)
39
- rect[0] = pts[np.argmin(s)] # Top-left
40
- rect[2] = pts[np.argmax(s)] # Bottom-right
41
-
42
- diff = np.diff(pts, axis=1)
43
- rect[1] = pts[np.argmin(diff)] # Top-right
44
- rect[3] = pts[np.argmax(diff)] # Bottom-left
45
-
46
- (tl, tr, br, bl) = rect
47
 
48
- widthA = np.linalg.norm(br - bl)
49
- widthB = np.linalg.norm(tr - tl)
50
- maxWidth = max(int(widthA), int(widthB))
51
 
52
- heightA = np.linalg.norm(tr - br)
53
- heightB = np.linalg.norm(tl - bl)
54
- maxHeight = max(int(heightA), int(heightB))
55
 
56
- dst = np.array([
57
- [0, 0],
58
- [maxWidth-1, 0],
59
- [maxWidth-1, maxHeight-1],
60
- [0, maxHeight-1]
61
- ], dtype="float32")
62
 
63
- M = cv2.getPerspectiveTransform(rect, dst)
64
- warped = cv2.warpPerspective(img, M, (maxWidth, maxHeight))
 
 
65
 
66
- return Image.fromarray(warped)
 
 
67
 
68
- def enhance_image(img_pil):
69
- """Enhance the image (brightness and contrast)."""
70
- enhancer_brightness = ImageEnhance.Brightness(img_pil)
71
- img_bright = enhancer_brightness.enhance(1.2)
72
 
73
- enhancer_contrast = ImageEnhance.Contrast(img_bright)
74
- img_contrast = enhancer_contrast.enhance(1.3)
 
75
 
76
- return img_contrast
 
 
 
77
 
78
- def rotate_image(img_pil, angle):
79
- """Rotate image."""
80
- return img_pil.rotate(angle, expand=True)
81
 
82
- # -----------------------
83
- # Streamlit App
84
- # -----------------------
 
 
 
85
 
86
- # Set app background and style
87
- page_bg_img = f"""
88
- <style>
89
- .stApp {{
90
- background-image: url("https://images.unsplash.com/photo-1581090700227-1c065c585ab3?ixlib=rb-4.0.3&auto=format&fit=crop&w=1470&q=80");
91
- background-size: cover;
92
- background-position: center;
93
- }}
94
- h1, h2, h3, h4, h5 {{
95
- color: white;
96
- }}
97
- </style>
98
- """
99
- st.markdown(page_bg_img, unsafe_allow_html=True)
100
 
101
- # App Title
102
- st.title("📄 Smart Document Scanner App")
 
 
 
103
 
104
- st.write("Upload your **ID cards or documents**. We'll detect, crop, enhance and generate a clean PDF!")
105
 
106
- uploaded_files = st.file_uploader(
107
- "Upload Images (JPG, JPEG, PNG supported)",
108
- type=["jpg", "jpeg", "png"],
109
- accept_multiple_files=True
110
- )
111
 
112
  if uploaded_files:
113
- final_images = []
 
114
 
115
- for file in uploaded_files:
116
- image = Image.open(file)
117
- st.subheader(f"Original Uploaded Image: {file.name}")
118
- st.image(image, caption="Original Image", use_column_width=True)
119
 
120
- # Auto crop
121
- cropped_img = auto_crop_document(image)
122
- st.image(cropped_img, caption="Auto Cropped", use_column_width=True)
123
 
124
- # Rotate option
125
- rotate_angle = st.selectbox(f"Rotate {file.name}?", [0, 90, 180, 270], key=f"rotate_{file.name}")
126
  if rotate_angle != 0:
127
- cropped_img = rotate_image(cropped_img, rotate_angle)
128
- st.image(cropped_img, caption=f"Rotated by {rotate_angle}°", use_column_width=True)
129
-
130
- # Enhance option
131
- if st.button(f"Enhance {file.name}", key=f"enhance_{file.name}"):
132
- enhanced_img = enhance_image(cropped_img)
133
- st.image(enhanced_img, caption="Enhanced Image (After)", use_column_width=True)
134
- final_images.append(enhanced_img)
135
- else:
136
- final_images.append(cropped_img)
137
 
138
- # Generate PDF
139
- if st.button("📥 Generate PDF"):
140
- pdf = FPDF()
141
- pdf.set_auto_page_break(0)
142
 
143
- for img in final_images:
144
- img_temp = io.BytesIO()
145
- img.save(img_temp, format='JPEG')
146
- img_temp.seek(0)
147
-
148
- cover = Image.open(img_temp)
149
- width, height = cover.size
150
-
151
- # Convert pixel in mm (1px = 0.264583 mm)
152
- width, height = float(width * 0.264583), float(height * 0.264583)
153
-
154
- pdf.add_page()
155
- pdf.image(img_temp, 0, 0, width, height)
156
-
157
- pdf_output = io.BytesIO()
158
- pdf.output(pdf_output)
159
- pdf_output.seek(0)
160
-
161
- timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
162
- filename = f"scanned_document_{timestamp}.pdf"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
- st.download_button(
165
- label="📄 Download Your PDF",
166
- data=pdf_output,
167
- file_name=filename,
168
- mime="application/pdf"
169
- )
 
 
 
1
  import streamlit as st
 
 
2
  from PIL import Image, ImageEnhance
3
+ import numpy as np
4
  import io
5
+ import cv2
6
  from fpdf import FPDF
7
+ import tempfile
8
+ import os
9
+ from rembg import remove
 
 
 
 
 
 
 
 
 
10
 
11
+ # --- Page config ---
12
+ st.set_page_config(page_title="Document Scanner App", page_icon="📄", layout="centered")
13
 
14
+ # --- Background image ---
15
+ page_bg_img = '''
16
+ <style>
17
+ .stApp {
18
+ background-image: url("https://images.unsplash.com/photo-1504150559088-41c99c17b7c6");
19
+ background-size: cover;
20
+ background-position: top left;
21
+ background-repeat: no-repeat;
22
+ background-attachment: fixed;
23
+ }
24
+ .block-container {
25
+ background-color: rgba(255,255,255,0.8);
26
+ border-radius: 10px;
27
+ padding: 2rem;
28
+ }
29
+ </style>
30
+ '''
31
+ st.markdown(page_bg_img, unsafe_allow_html=True)
 
 
 
 
32
 
33
+ # --- Title ---
34
+ st.title("📄 Smart Document Scanner")
35
+ st.write("Upload photos of ID Cards, Documents taken from mobile & get them auto-cropped, enhanced, and converted to PDF!")
36
 
37
+ # --- Functions ---
 
 
38
 
39
+ def auto_crop_image(image):
40
+ # Convert to grayscale
41
+ gray = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2GRAY)
42
+ blur = cv2.GaussianBlur(gray, (5,5), 0)
43
+ edged = cv2.Canny(blur, 50, 150)
 
44
 
45
+ # Find contours
46
+ contours, _ = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
47
+ if not contours:
48
+ return image
49
 
50
+ # Find the biggest contour
51
+ c = max(contours, key=cv2.contourArea)
52
+ x,y,w,h = cv2.boundingRect(c)
53
 
54
+ # Crop and return
55
+ cropped = image.crop((x,y,x+w,y+h))
56
+ return cropped
 
57
 
58
+ def enhance_image(image):
59
+ enhancer = ImageEnhance.Contrast(image)
60
+ image = enhancer.enhance(1.5)
61
 
62
+ enhancer = ImageEnhance.Sharpness(image)
63
+ image = enhancer.enhance(2.0)
64
+
65
+ return image
66
 
67
+ def rotate_image(image, angle):
68
+ return image.rotate(angle, expand=True)
 
69
 
70
+ def flip_image(image, mode):
71
+ if mode == "Horizontal":
72
+ return image.transpose(Image.FLIP_LEFT_RIGHT)
73
+ elif mode == "Vertical":
74
+ return image.transpose(Image.FLIP_TOP_BOTTOM)
75
+ return image
76
 
77
+ # --- Sidebar options ---
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
+ st.sidebar.header("⚙️ Settings")
80
+ crop_mode = st.sidebar.selectbox("Cropping Mode", ["Auto Crop", "Manual Crop"])
81
+ apply_enhance = st.sidebar.checkbox("Enhance Image (contrast, sharpness)", value=False)
82
+ rotate_angle = st.sidebar.selectbox("Rotate", [0, 90, 180, 270])
83
+ flip_option = st.sidebar.selectbox("Flip", ["None", "Horizontal", "Vertical"])
84
 
85
+ # --- Upload images ---
86
 
87
+ uploaded_files = st.file_uploader("Upload images", type=["jpg", "jpeg", "png"], accept_multiple_files=True)
 
 
 
 
88
 
89
  if uploaded_files:
90
+ st.success(f"✅ {len(uploaded_files)} file(s) uploaded successfully!")
91
+ cleaned_images = []
92
 
93
+ progress = st.progress(0)
 
 
 
94
 
95
+ for idx, uploaded_file in enumerate(uploaded_files):
96
+ image = Image.open(uploaded_file).convert("RGB")
 
97
 
98
+ # Apply rotation
 
99
  if rotate_angle != 0:
100
+ image = rotate_image(image, rotate_angle)
 
 
 
 
 
 
 
 
 
101
 
102
+ # Apply flip
103
+ if flip_option != "None":
104
+ image = flip_image(image, flip_option)
 
105
 
106
+ # Crop
107
+ if crop_mode == "Auto Crop":
108
+ image = auto_crop_image(image)
109
+ else:
110
+ # Manual Crop
111
+ st.write(f"✂️ Manually crop for {uploaded_file.name}")
112
+ cropped_area = st.slider(f"Adjust crop for {uploaded_file.name}", 0, 100, (5,95), key=idx)
113
+ width, height = image.size
114
+ left = width * cropped_area[0] // 100
115
+ right = width * cropped_area[1] // 100
116
+ image = image.crop((left, 0, right, height))
117
+
118
+ # Enhance if selected
119
+ if apply_enhance:
120
+ image = enhance_image(image)
121
+
122
+ st.image(image, caption=f"Processed: {uploaded_file.name}", use_container_width=True)
123
+
124
+ cleaned_images.append(image)
125
+
126
+ progress.progress((idx+1)/len(uploaded_files))
127
+
128
+ # --- Generate PDF ---
129
+ st.subheader("📄 Download PDF")
130
+
131
+ if st.button("Generate PDF"):
132
+ with tempfile.TemporaryDirectory() as tmpdirname:
133
+ pdf = FPDF(unit="pt", format="A4")
134
+ for img_idx, img in enumerate(cleaned_images):
135
+ temp_img_path = os.path.join(tmpdirname, f"img_{img_idx}.jpg")
136
+ img.save(temp_img_path, format="JPEG")
137
+
138
+ # Calculate aspect ratio
139
+ img_width, img_height = img.size
140
+ page_width = pdf.w - 40 # 20 margin each side
141
+ ratio = page_width / img_width
142
+ new_width = page_width
143
+ new_height = img_height * ratio
144
+
145
+ pdf.add_page()
146
+ pdf.image(temp_img_path, 20, 20, new_width, new_height)
147
+
148
+ pdf_output_path = os.path.join(tmpdirname, "scanned_document.pdf")
149
+ pdf.output(pdf_output_path)
150
+
151
+ with open(pdf_output_path, "rb") as f:
152
+ st.download_button(
153
+ label="📥 Download Scanned PDF",
154
+ data=f,
155
+ file_name="scanned_document.pdf",
156
+ mime="application/pdf"
157
+ )
158