arifdogan commited on
Commit
39e0658
·
verified ·
1 Parent(s): c6c8d7f

Add .idea config files, update README with detailed instructions, implement invoice extraction in app.py, and list dependencies in requirements.txt.

Browse files
.idea/.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Editor-based HTTP Client requests
5
+ /httpRequests/
6
+ # Datasource local storage ignored files
7
+ /dataSources/
8
+ /dataSources.local.xml
.idea/inspectionProfiles/profiles_settings.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="PROJECT_PROFILE" value="Laravel" />
4
+ <option name="USE_PROJECT_PROFILE" value="false" />
5
+ </settings>
6
+ </component>
.idea/invoice-ai-extractor.iml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
6
+ </content>
7
+ <orderEntry type="jdk" jdkName="Python 3.11 (invoice-ai-extractor)" jdkType="Python SDK" />
8
+ <orderEntry type="sourceFolder" forTests="false" />
9
+ </component>
10
+ </module>
.idea/misc.xml ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.11 (invoice-ai-extractor)" />
5
+ </component>
6
+ <component name="ComposerSettings">
7
+ <execution />
8
+ </component>
9
+ <component name="ProjectInspectionProfilesVisibleTreeState">
10
+ <entry key="Customized">
11
+ <profile-state>
12
+ <expanded-state>
13
+ <State />
14
+ <State>
15
+ <id>Angular</id>
16
+ </State>
17
+ <State>
18
+ <id>GeneralJavaScript</id>
19
+ </State>
20
+ <State>
21
+ <id>JavaScript</id>
22
+ </State>
23
+ <State>
24
+ <id>PHP</id>
25
+ </State>
26
+ <State>
27
+ <id>Type compatibilityPHP</id>
28
+ </State>
29
+ </expanded-state>
30
+ <selected-state>
31
+ <State>
32
+ <id>AngularAmbiguousComponentTag</id>
33
+ </State>
34
+ </selected-state>
35
+ </profile-state>
36
+ </entry>
37
+ <entry key="Project Default">
38
+ <profile-state>
39
+ <expanded-state>
40
+ <State />
41
+ <State>
42
+ <id>Angular</id>
43
+ </State>
44
+ </expanded-state>
45
+ <selected-state>
46
+ <State>
47
+ <id>AngularInaccessibleComponentMemberInAotMode</id>
48
+ </State>
49
+ </selected-state>
50
+ </profile-state>
51
+ </entry>
52
+ </component>
53
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (invoice-ai-extractor)" project-jdk-type="Python SDK" />
54
+ </project>
.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/invoice-ai-extractor.iml" filepath="$PROJECT_DIR$/.idea/invoice-ai-extractor.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
README.md CHANGED
@@ -1,14 +1,40 @@
1
- ---
2
- title: Invoice Ai Extractor
3
- emoji: 🐨
4
- colorFrom: yellow
5
- colorTo: pink
6
- sdk: streamlit
7
- sdk_version: 1.42.2
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- short_description: Extract key data from invoices with LayoutLM.
12
- ---
 
 
 
 
 
 
 
 
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Invoice AI Extractor 🧾
2
+
3
+ An AI-powered invoice information extraction tool that automatically extracts key information from your invoices using
4
+ LayoutLM.
5
+
6
+ ## Features
7
+
8
+ - Upload PDFs, JPGs, or PNGs 📄
9
+ - Paste images directly from clipboard 📎
10
+ - Extract key information:
11
+ - Invoice Numbers
12
+ - Dates
13
+ - Total Amounts
14
+ - Company Names
15
+ - Due Dates
16
+ - Tax Amounts
17
+ - View confidence scores for each extracted field
18
+ - Export results in JSON, CSV, or TXT format
19
+
20
+ ## How to Use
21
 
22
+ 1. Upload your invoice using the file uploader or paste from clipboard
23
+ 2. Wait for the AI to process your document
24
+ 3. View the extracted information with confidence scores
25
+ 4. Download the results in your preferred format
26
+
27
+ ## Model
28
+
29
+ Uses LayoutLM fine-tuned on invoice data to understand and extract information from document images. The model combines
30
+ text, layout, and image information for accurate extraction.
31
+
32
+ ## Tips
33
+
34
+ - Ensure documents are clearly scanned or photographed
35
+ - Supported formats: PDF, JPG, PNG
36
+ - Maximum file size: 10MB
37
+ - Supported language: English
38
+
39
+ ---
40
+ Created by [Arif] | [GitHub](https://github.com/doganarif)
app.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import json
3
+
4
+ import fitz
5
+ import streamlit as st
6
+ import torch
7
+ from PIL import Image, ImageGrab
8
+ from transformers import pipeline
9
+
10
+ # --- Configuration and Setup ---
11
+
12
+ DEVICE = 0 if torch.cuda.is_available() else -1
13
+
14
+ st.set_page_config(
15
+ page_title="Invoice AI | by Arif Dogan",
16
+ page_icon="🧾",
17
+ layout="wide",
18
+ initial_sidebar_state="collapsed",
19
+ )
20
+
21
+ # --- Styling ---
22
+
23
+ st.markdown(
24
+ """
25
+ <style>
26
+ .stApp {max-width: 1200px; margin: 0 auto}
27
+ .stButton>button {background-color: #4CAF50; color: white; border-radius: 5px;}
28
+ .stProgress>div>div {background-color: #4CAF50}
29
+ footer {visibility: hidden}
30
+ .high {color: #4CAF50; font-weight: bold}
31
+ .medium {color: #FFA726; font-weight: bold}
32
+ .low {color: #EF5350; font-weight: bold}
33
+ div[data-testid="stToolbar"] {visibility: hidden; height: 0}
34
+ [data-testid="stExpanderContent"] {background-color: rgba(67, 76, 94, 0.5);}
35
+ .stTextInput>div>div {background-color: rgba(67, 76, 94, 0.5)}
36
+ </style>
37
+ """,
38
+ unsafe_allow_html=True,
39
+ )
40
+
41
+
42
+ # --- Functions ---
43
+
44
+
45
+ @st.cache_resource
46
+ def load_model():
47
+ return pipeline(
48
+ "document-question-answering",
49
+ model="faisalraza/layoutlm-invoices",
50
+ device=DEVICE,
51
+ )
52
+
53
+
54
+ def process_pdf(pdf_file):
55
+ pdf_content = pdf_file.read()
56
+ pdf_stream = io.BytesIO(pdf_content)
57
+ try:
58
+ with fitz.open(stream=pdf_stream, filetype="pdf") as pdf_document:
59
+ if pdf_document.page_count > 0:
60
+ page = pdf_document[0]
61
+ pix = page.get_pixmap(matrix=fitz.Matrix(300 / 72, 300 / 72))
62
+ img_data = pix.tobytes("png")
63
+ return Image.open(io.BytesIO(img_data)), pdf_document.page_count
64
+ else:
65
+ raise ValueError("PDF has no pages")
66
+ except Exception as e:
67
+ raise e
68
+ finally:
69
+ pdf_stream.close()
70
+
71
+
72
+ def process_image(uploaded_file):
73
+ uploaded_file.seek(0)
74
+ if uploaded_file.type == "application/pdf":
75
+ return process_pdf(uploaded_file)
76
+ return Image.open(uploaded_file), 1
77
+
78
+
79
+ def get_clipboard_image():
80
+ try:
81
+ img = ImageGrab.grabclipboard()
82
+ return (img, 1) if isinstance(img, Image.Image) else (None, 0)
83
+ except Exception:
84
+ return None, 0
85
+
86
+
87
+ def prepare_export_data(extracted_info, format_type):
88
+ if format_type == "JSON":
89
+ return json.dumps(
90
+ {field: data["value"] for field, data in extracted_info.items()}, indent=2
91
+ )
92
+ elif format_type == "CSV":
93
+ header = ",".join(extracted_info.keys())
94
+ values = ",".join(f'"{data["value"]}"' for data in extracted_info.values())
95
+ return f"{header}\n{values}"
96
+ else: # TXT
97
+ return "\n".join(
98
+ f"{field}: {data['value']}" for field, data in extracted_info.items()
99
+ )
100
+
101
+
102
+ def extract_information(model, image, questions, progress_bar, status_text):
103
+ extracted_info = {}
104
+ for idx, question in enumerate(questions):
105
+ try:
106
+ # Update progress bar and status text
107
+ progress_bar.progress((idx + 1) / len(questions))
108
+ status_text.text(f"Processing: {question} ({idx + 1}/{len(questions)})")
109
+
110
+ response = model(image=image, question=question)
111
+ if (
112
+ response and response[0].get("answer", "").strip()
113
+ ): # Check for non-empty answer
114
+ answer = response[0]["answer"]
115
+ confidence = response[0]["score"]
116
+
117
+ if confidence > 0.1:
118
+ field = (
119
+ question.replace("What is the ", "").replace("?", "").title()
120
+ )
121
+ extracted_info[field] = {"value": answer, "confidence": confidence}
122
+ except Exception:
123
+ continue # Handle potential errors during model processing
124
+
125
+ return extracted_info
126
+
127
+
128
+ # --- Initialization ---
129
+
130
+ if "processed_image" not in st.session_state:
131
+ st.session_state.processed_image = None
132
+ if "extracted_info" not in st.session_state:
133
+ st.session_state.extracted_info = {}
134
+
135
+ # --- UI Layout ---
136
+
137
+ st.markdown(
138
+ """
139
+ <div style='text-align: center; padding: 1rem;'>
140
+ <h1>🧾 Invoice AI Extractor</h1>
141
+ <p style='font-size: 1.2em; color: #999;'>Powered by LayoutLM</p>
142
+ </div>
143
+ """,
144
+ unsafe_allow_html=True,
145
+ )
146
+
147
+ model = load_model()
148
+
149
+ col1, col2 = st.columns([2, 1])
150
+
151
+ with col1:
152
+ uploaded_file = st.file_uploader(
153
+ "Drop invoice (PDF, JPG, PNG)", type=["pdf", "jpg", "jpeg", "png"]
154
+ )
155
+
156
+ with col2:
157
+ st.write("Or paste from clipboard (Ctrl/Cmd + V)")
158
+ check_clipboard = st.button("📎 Check Clipboard")
159
+
160
+ # --- Image Processing Logic ---
161
+
162
+ if uploaded_file:
163
+ try:
164
+ image, _ = process_image(uploaded_file)
165
+ st.session_state.processed_image = image
166
+ st.session_state.extracted_info = {} # Reset on new upload
167
+ except Exception as e:
168
+ st.error(f"Error processing file: {e}")
169
+
170
+ elif check_clipboard:
171
+ clipboard_image, _ = get_clipboard_image()
172
+ if clipboard_image:
173
+ st.session_state.processed_image = clipboard_image
174
+ st.session_state.extracted_info = {}
175
+ st.success("Image loaded from clipboard")
176
+ else:
177
+ st.warning("No image found in clipboard")
178
+
179
+ # --- Display and Information Extraction ---
180
+
181
+ if st.session_state.processed_image:
182
+ try:
183
+ image = st.session_state.processed_image.convert("RGB")
184
+
185
+ col1, col2 = st.columns([1, 1])
186
+
187
+ with col1:
188
+ st.image(image, caption="Document", use_container_width=True)
189
+
190
+ with col2:
191
+ st.markdown("### 📊 Extracted Information")
192
+
193
+ if not st.session_state.extracted_info:
194
+ questions = [
195
+ "What is the invoice number?",
196
+ "What is the invoice date?",
197
+ "What is the total amount?",
198
+ "What is the company name?",
199
+ "What is the due date?",
200
+ "What is the tax amount?",
201
+ ]
202
+
203
+ # Create progress bar and status text elements
204
+ progress_bar = st.progress(0)
205
+ status_text = st.empty()
206
+
207
+ st.session_state.extracted_info = extract_information(
208
+ model, image, questions, progress_bar, status_text
209
+ )
210
+
211
+ # Clear status text after completion
212
+ status_text.empty()
213
+
214
+ if st.session_state.extracted_info:
215
+ for field, data in st.session_state.extracted_info.items():
216
+ conf_col, val_col = st.columns([1, 4])
217
+ with val_col:
218
+ st.text_input(
219
+ field, data["value"], disabled=True, key=f"input_{field}"
220
+ ) # added key
221
+ with conf_col:
222
+ confidence = data["confidence"]
223
+ css_class = (
224
+ "high"
225
+ if confidence > 0.7
226
+ else "medium"
227
+ if confidence > 0.4
228
+ else "low"
229
+ )
230
+ st.markdown(
231
+ f"<p class='{css_class}'>{confidence:.1%}</p>",
232
+ unsafe_allow_html=True,
233
+ )
234
+
235
+ st.markdown("### 📥 Export")
236
+ export_format = st.selectbox("Format", ["JSON", "CSV", "TXT"])
237
+ export_data = prepare_export_data(
238
+ st.session_state.extracted_info, export_format
239
+ )
240
+ file_extension = export_format.lower()
241
+ st.download_button(
242
+ "Download",
243
+ export_data,
244
+ file_name=f"invoice_data.{file_extension}",
245
+ mime=f"text/{file_extension}",
246
+ )
247
+ else:
248
+ st.warning(
249
+ "Could not extract information. Please ensure the document is clear."
250
+ )
251
+
252
+ except Exception as e:
253
+ st.error(f"Error during processing: {e}")
254
+
255
+ # --- Footer ---
256
+
257
+ st.markdown("---")
258
+ st.markdown(
259
+ """
260
+ <div style='text-align: center'>
261
+ <p>Created by <a href='https://github.com/doganarif' target='_blank'>Arif Dogan</a> |
262
+ <a href='https://huggingface.co/arifdogan' target='_blank'>🤗 Hugging Face</a></p>
263
+ </div>
264
+ """,
265
+ unsafe_allow_html=True,
266
+ )
requirements.txt ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ asgiref==3.8.1
2
+ attrs==24.3.0
3
+ certifi==2024.12.14
4
+ cffi==1.17.1
5
+ charset-normalizer==3.4.0
6
+ cryptography==44.0.1
7
+ Deprecated==1.2.18
8
+ Django==5.1.4
9
+ djangorestframework==3.15.2
10
+ filelock==3.16.1
11
+ fsspec==2024.12.0
12
+ git-filter-repo==2.45.0
13
+ h11==0.14.0
14
+ huggingface-hub==0.27.0
15
+ idna==3.10
16
+ inquirerpy==0.3.4
17
+ isodate==0.7.2
18
+ lxml==5.3.0
19
+ outcome==1.3.0.post0
20
+ packaging==24.2
21
+ pfzy==0.3.4
22
+ prompt_toolkit==3.0.48
23
+ pycountry==24.6.1
24
+ pycparser==2.22
25
+ pycryptodome==3.21.0
26
+ PyGithub==2.5.0
27
+ PyJWT==2.10.1
28
+ PyNaCl==1.5.0
29
+ PySocks==1.7.1
30
+ python-magic==0.4.27
31
+ PyYAML==6.0.2
32
+ requests==2.32.3
33
+ selenium==4.27.1
34
+ sniffio==1.3.1
35
+ socks==0
36
+ sortedcontainers==2.4.0
37
+ sqlparse==0.5.2
38
+ streamlink==7.0.0
39
+ tqdm==4.67.1
40
+ trio==0.27.0
41
+ trio-websocket==0.11.1
42
+ typing_extensions==4.12.2
43
+ undetected-chromedriver==3.5.5
44
+ urllib3==2.2.3
45
+ wcwidth==0.2.13
46
+ websocket-client==1.8.0
47
+ websockets==14.1
48
+ wrapt==1.17.2
49
+ wsproto==1.2.0
50
+