UI and backend for basic OCR functionality
This commit is contained in:
BIN
backend/__pycache__/main.cpython-313.pyc
Normal file
BIN
backend/__pycache__/main.cpython-313.pyc
Normal file
Binary file not shown.
20
backend/generate_tests.py
Normal file
20
backend/generate_tests.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from reportlab.pdfgen import canvas
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
def create_pdf(filename, text):
|
||||
c = canvas.Canvas(filename)
|
||||
c.drawString(100, 750, text)
|
||||
c.save()
|
||||
print(f"Created {filename}")
|
||||
|
||||
def create_image(filename, text):
|
||||
img = Image.new('RGB', (400, 100), color = (255, 255, 255))
|
||||
d = ImageDraw.Draw(img)
|
||||
# Default font is usually tiny, but readable by tesseract
|
||||
d.text((10,10), text, fill=(0,0,0))
|
||||
img.save(filename)
|
||||
print(f"Created {filename}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_pdf("test_ocr.pdf", "Hello World PDF OCR")
|
||||
create_image("test_ocr.png", "Hello World Image OCR")
|
||||
105
backend/main.py
Normal file
105
backend/main.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import io
|
||||
from typing import Optional
|
||||
from fastapi import FastAPI, File, UploadFile, HTTPException, Depends
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from pypdf import PdfReader
|
||||
import pytesseract
|
||||
from PIL import Image
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# CORS configuration
|
||||
origins = [
|
||||
"http://localhost",
|
||||
"http://localhost:3000",
|
||||
"http://localhost:5173", # Vite default
|
||||
"http://localhost:4200", # Angular default
|
||||
"http://127.0.0.1:4200",
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Models
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
token: str
|
||||
message: str
|
||||
|
||||
class NERResponse(BaseModel):
|
||||
text: str
|
||||
|
||||
# 1. User Login Module (Dummy)
|
||||
@app.post("/api/login", response_model=LoginResponse)
|
||||
async def login(request: LoginRequest):
|
||||
if request.username == "admin" and request.password == "admin":
|
||||
return LoginResponse(token="dummy-jwt-token-123", message="Login Successful")
|
||||
raise HTTPException(status_code=401, detail="Invalid credentials")
|
||||
|
||||
# 2. OCR Module
|
||||
def extract_text_from_pdf(file_bytes: bytes) -> str:
|
||||
try:
|
||||
reader = PdfReader(io.BytesIO(file_bytes))
|
||||
text = ""
|
||||
for page in reader.pages:
|
||||
page_text = page.extract_text()
|
||||
if page_text:
|
||||
text += page_text + "\n"
|
||||
return text.strip()
|
||||
except Exception as e:
|
||||
print(f"Error reading PDF: {e}")
|
||||
return ""
|
||||
|
||||
def extract_text_from_image(file_bytes: bytes) -> str:
|
||||
try:
|
||||
image = Image.open(io.BytesIO(file_bytes))
|
||||
text = pytesseract.image_to_string(image)
|
||||
return text.strip()
|
||||
except Exception as e:
|
||||
print(f"Error reading Image: {e}")
|
||||
return ""
|
||||
|
||||
from pdf2image import convert_from_bytes
|
||||
|
||||
# ... (imports)
|
||||
|
||||
@app.post("/api/ocr/extract", response_model=NERResponse)
|
||||
async def extract_text(file: UploadFile = File(...)):
|
||||
content = await file.read()
|
||||
filename = file.filename.lower()
|
||||
|
||||
extracted_text = ""
|
||||
|
||||
if filename.endswith(".pdf"):
|
||||
# Try text extraction first
|
||||
extracted_text = extract_text_from_pdf(content)
|
||||
|
||||
# If text is empty, it might be a scanned PDF.
|
||||
if not extracted_text.strip():
|
||||
try:
|
||||
images = convert_from_bytes(content)
|
||||
for i, image in enumerate(images):
|
||||
page_text = pytesseract.image_to_string(image)
|
||||
extracted_text += f"\n--- Page {i+1} ---\n{page_text}"
|
||||
except Exception as e:
|
||||
extracted_text = f"Error processing Scanned PDF: {str(e)}\n\n(Hint: Ensure 'poppler' is installed on your system. Run 'brew install poppler')"
|
||||
|
||||
elif filename.endswith((".png", ".jpg", ".jpeg", ".tiff", ".bmp")):
|
||||
extracted_text = extract_text_from_image(content)
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail="Unsupported file type")
|
||||
|
||||
return NERResponse(text=extracted_text)
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"message": "OCR Backend API is running"}
|
||||
6
backend/requirements.txt
Normal file
6
backend/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
fastapi
|
||||
uvicorn
|
||||
python-multipart
|
||||
pypdf
|
||||
pytesseract
|
||||
Pillow
|
||||
1
backend/test.txt
Normal file
1
backend/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a test PDF content
|
||||
68
backend/test_ocr.pdf
Normal file
68
backend/test_ocr.pdf
Normal file
@@ -0,0 +1,68 @@
|
||||
%PDF-1.3
|
||||
%<25><><EFBFBD><EFBFBD> ReportLab Generated PDF document (opensource)
|
||||
1 0 obj
|
||||
<<
|
||||
/F1 2 0 R
|
||||
>>
|
||||
endobj
|
||||
2 0 obj
|
||||
<<
|
||||
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||
>>
|
||||
endobj
|
||||
3 0 obj
|
||||
<<
|
||||
/Contents 7 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 6 0 R /Resources <<
|
||||
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||
>> /Rotate 0 /Trans <<
|
||||
|
||||
>>
|
||||
/Type /Page
|
||||
>>
|
||||
endobj
|
||||
4 0 obj
|
||||
<<
|
||||
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
5 0 obj
|
||||
<<
|
||||
/Author (anonymous) /CreationDate (D:20260121205329+05'00') /Creator (anonymous) /Keywords () /ModDate (D:20260121205329+05'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||
/Subject (unspecified) /Title (untitled) /Trapped /False
|
||||
>>
|
||||
endobj
|
||||
6 0 obj
|
||||
<<
|
||||
/Count 1 /Kids [ 3 0 R ] /Type /Pages
|
||||
>>
|
||||
endobj
|
||||
7 0 obj
|
||||
<<
|
||||
/Filter [ /ASCII85Decode /FlateDecode ] /Length 115
|
||||
>>
|
||||
stream
|
||||
GapQh0E=F,0U\H3T\pNYT^QKk?tc>IP,;W#U1^23ihPEM_?CW4KISi90MjG^2,FS#<RC5+c,n(/#gY0H8Pob4EDu@N%an;^a\iDl!bkQj!W^(m'ef~>endstream
|
||||
endobj
|
||||
xref
|
||||
0 8
|
||||
0000000000 65535 f
|
||||
0000000061 00000 n
|
||||
0000000092 00000 n
|
||||
0000000199 00000 n
|
||||
0000000402 00000 n
|
||||
0000000470 00000 n
|
||||
0000000731 00000 n
|
||||
0000000790 00000 n
|
||||
trailer
|
||||
<<
|
||||
/ID
|
||||
[<6da718d498e92fec48910ff2674542d2><6da718d498e92fec48910ff2674542d2>]
|
||||
% ReportLab generated PDF document -- digest (opensource)
|
||||
|
||||
/Info 5 0 R
|
||||
/Root 4 0 R
|
||||
/Size 8
|
||||
>>
|
||||
startxref
|
||||
995
|
||||
%%EOF
|
||||
BIN
backend/test_ocr.png
Normal file
BIN
backend/test_ocr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Reference in New Issue
Block a user