mashaelalbu commited on
Commit
e7d0dbe
·
verified ·
1 Parent(s): 2716d8e

Upload 10 files

Browse files
Files changed (10) hide show
  1. .gitattributes +35 -35
  2. Dockerfile +32 -0
  3. README.md +12 -10
  4. app.py +6 -0
  5. app/__init__.py +9 -0
  6. app/config.py +6 -0
  7. app/routes.py +140 -0
  8. app/utils.py +105 -0
  9. docker-compose.yml +21 -0
  10. requirements.txt +8 -0
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1
4
+ ENV PYTHONUNBUFFERED=1
5
+ ENV DEBIAN_FRONTEND=noninteractive
6
+ ENV MODEL_PATH=RufusRubin777/GOT-OCR2_0_CPU
7
+
8
+ WORKDIR /app
9
+
10
+ RUN apt-get update && apt-get install -y \
11
+ build-essential \
12
+ libpq-dev \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ COPY requirements.txt .
16
+ RUN pip install --no-cache-dir -r requirements.txt
17
+
18
+ RUN mkdir -p /root/.cache/huggingface
19
+ VOLUME /root/.cache/huggingface
20
+
21
+ RUN python -c "from transformers_modules.RufusRubin777.GOT_OCR2_0_CPU.modeling_GOT import GOTModel, GOTConfig; \
22
+ from transformers import AutoTokenizer; \
23
+ model_path='RufusRubin777/GOT-OCR2_0_CPU'; \
24
+ config = GOTConfig.from_pretrained(model_path); \
25
+ model = GOTModel.from_pretrained(model_path, config=config); \
26
+ tokenizer = AutoTokenizer.from_pretrained(model_path)"
27
+
28
+ COPY . .
29
+
30
+ EXPOSE 7860
31
+
32
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -1,10 +1,12 @@
1
- ---
2
- title: Ocrsensitive
3
- emoji: 📚
4
- colorFrom: gray
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
+ ---
2
+ title: Ocrfood
3
+ emoji: 🏢
4
+ colorFrom: pink
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 5.6.0
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from app import create_app
2
+
3
+ app = create_app()
4
+
5
+ if __name__ == '__main__':
6
+ app.run(host='0.0.0.0', port=7860)
app/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask
2
+
3
+ def create_app():
4
+ app = Flask(__name__)
5
+
6
+ from app.routes import main
7
+ app.register_blueprint(main)
8
+
9
+ return app
app/config.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ class Config:
4
+ SECRET_KEY = os.environ.get('SECRET_KEY') or 'your-secret-key'
5
+ UPLOAD_FOLDER = 'instance/uploads'
6
+ MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max file size
app/routes.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # routes.py
2
+ from flask import Blueprint, jsonify, request
3
+ import io
4
+ from app.utils import OCRModel
5
+ import logging
6
+
7
+ logging.basicConfig(level=logging.INFO)
8
+ logger = logging.getLogger(__name__)
9
+
10
+ main = Blueprint('main', __name__)
11
+ ocr_model = OCRModel()
12
+
13
+ ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
14
+
15
+ KNOWN_ALLERGENS = {
16
+ 'gluten': ['wheat', 'barley', 'rye', 'oats', 'gluten', 'flour', 'bread', 'pasta'],
17
+ 'dairy': ['milk', 'yogurt', 'cheese', 'lactose', 'cream', 'butter', 'whey'],
18
+ 'nuts': ['nuts', 'peanuts', 'almonds', 'walnuts', 'cashews', 'pistachios'],
19
+ 'eggs': ['eggs', 'egg', 'albumin', 'mayonnaise'],
20
+ 'soy': ['soy', 'soybeans', 'tofu', 'edamame'],
21
+ 'fish': ['fish', 'salmon', 'tuna', 'cod', 'tilapia'],
22
+ 'shellfish': ['shellfish', 'shrimp', 'crab', 'lobster', 'oyster', 'mussels'],
23
+ 'sesame': ['sesame', 'tahini'],
24
+ 'mustard': ['mustard'],
25
+ 'celery': ['celery'],
26
+ 'lupin': ['lupin'],
27
+ 'sulfites': ['sulfites', 'sulphites']
28
+ }
29
+
30
+ def allowed_file(filename):
31
+ """Validate file extension"""
32
+ return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
33
+
34
+ def find_allergens(text, user_allergens):
35
+ """Finding Sensitivities in the Text"""
36
+ text = text.lower()
37
+ found_allergens = set()
38
+ allergen_details = {}
39
+ allergen_locations = {}
40
+
41
+ for allergen in user_allergens:
42
+ allergen = allergen.strip().lower()
43
+ if allergen in KNOWN_ALLERGENS:
44
+ for variant in KNOWN_ALLERGENS[allergen]:
45
+ if variant.lower() in text:
46
+ found_allergens.add(allergen)
47
+ allergen_details[allergen] = variant
48
+ # Store the location of the word in the text
49
+ allergen_locations[allergen] = text.index(variant.lower())
50
+ elif allergen in text:
51
+ found_allergens.add(allergen)
52
+ allergen_details[allergen] = allergen
53
+ allergen_locations[allergen] = text.index(allergen)
54
+
55
+ return found_allergens, allergen_details, allergen_locations
56
+
57
+ @main.route('/')
58
+ def index():
59
+ return jsonify({
60
+ "message": "Welcome to the Text Recognition and Sensitivity Checking Service",
61
+ "endpoints": {
62
+ "/api/ocr": "POST - Image analysis and sensitivity testing",
63
+ "/api/allergens": "GET - List of known allergens"
64
+ },
65
+ "supported_formats": list(ALLOWED_EXTENSIONS),
66
+ "known_allergens": list(KNOWN_ALLERGENS.keys())
67
+ })
68
+
69
+ @main.route('/api/ocr', methods=['POST'])
70
+ def process_image():
71
+ try:
72
+ # Check if file exists
73
+ if 'file' not in request.files:
74
+ logger.warning("No file uploaded")
75
+ return jsonify({"error": "No file uploaded"}), 400
76
+
77
+ # Check the list of allergies
78
+ if 'allergens' not in request.form:
79
+ logger.warning("Allergens not specified")
80
+ return jsonify({"error": "Allergens not specified"}), 400
81
+
82
+ file = request.files['file']
83
+ if file.filename == '':
84
+ logger.warning("No file selected")
85
+ return jsonify({"error": "No file selected"}), 400
86
+
87
+ # Check file type
88
+ if not allowed_file(file.filename):
89
+ logger.warning(f"Invalid file type: {file.filename}")
90
+ return jsonify({
91
+ "error": "File type not supported",
92
+ "supported_formats": list(ALLOWED_EXTENSIONS)
93
+ }), 400
94
+
95
+ # Prepare a list of allergies.
96
+ user_allergens = request.form['allergens'].split(',')
97
+ logger.info(f"Processing image for allergens: {user_allergens}")
98
+
99
+ # Read image
100
+ file_bytes = file.read()
101
+ file_stream = io.BytesIO(file_bytes)
102
+
103
+ # Processing image
104
+ extracted_text = ocr_model.process_image(file_stream)
105
+ logger.info(f"Extracted text: {extracted_text}")
106
+
107
+ # Searching for allergies
108
+ found_allergens, allergen_details, allergen_locations = find_allergens(extracted_text, user_allergens)
109
+
110
+ # Response
111
+ response = {
112
+ "success": True,
113
+ "extracted_text": extracted_text,
114
+ "analysis": {
115
+ "found_allergens": list(found_allergens),
116
+ "allergen_details": allergen_details,
117
+ "allergen_locations": allergen_locations,
118
+ "has_allergens": len(found_allergens) > 0,
119
+ "warning": "⚠️ Warning: Allergens found!" if found_allergens else "✅ No allergens found",
120
+ "severity": "high" if len(found_allergens) > 0 else "none"
121
+ }
122
+ }
123
+
124
+ logger.info(f"Analysis completed successfully: {found_allergens}")
125
+ return jsonify(response)
126
+
127
+ except Exception as e:
128
+ logger.error(f"Error processing request: {str(e)}", exc_info=True)
129
+ return jsonify({
130
+ "error": "An error occurred while processing the image.",
131
+ "details": str(e)
132
+ }), 500
133
+
134
+ @main.route('/api/allergens', methods=['GET'])
135
+ def get_known_allergens():
136
+ return jsonify({
137
+ "allergens": KNOWN_ALLERGENS,
138
+ "total_count": len(KNOWN_ALLERGENS),
139
+ "last_updated": "2024-03-24" # Update this date when the list is updated.
140
+ })
app/utils.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # utils.py
2
+ import os
3
+ from transformers import AutoModel, AutoTokenizer
4
+ from PIL import Image, ImageEnhance, ImageFilter
5
+ import torch
6
+ import logging
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ class OCRModel:
11
+ _instance = None
12
+
13
+ def __new__(cls):
14
+ if cls._instance is None:
15
+ cls._instance = super(OCRModel, cls).__new__(cls)
16
+ cls._instance.initialize()
17
+ return cls._instance
18
+
19
+ def initialize(self):
20
+ try:
21
+ logger.info("Initializing OCR model...")
22
+
23
+ # Model initilization
24
+ self.tokenizer = AutoTokenizer.from_pretrained('RufusRubin777/GOT-OCR2_0_CPU', trust_remote_code=True)
25
+ self.model = AutoModel.from_pretrained(
26
+ 'RufusRubin777/GOT-OCR2_0_CPU',
27
+ trust_remote_code=True,
28
+ low_cpu_mem_usage=True,
29
+ device_map='cpu',
30
+ use_safetensors=True,
31
+ pad_token_id=self.tokenizer.eos_token_id
32
+ )
33
+
34
+ # choose cpu
35
+ self.device = "cpu"
36
+ self.model = self.model.eval().cpu()
37
+
38
+ logger.info("Model initialization completed successfully")
39
+
40
+ except Exception as e:
41
+ logger.error(f"Error initializing model: {str(e)}", exc_info=True)
42
+ raise
43
+
44
+ def preprocess_image(self, image):
45
+ """Image preprocessing to improve text recognition quality"""
46
+ try:
47
+ # Convert image to RGB if it is not already
48
+ if image.mode != 'RGB':
49
+ image = image.convert('RGB')
50
+
51
+ # Improve contrast
52
+ enhancer = ImageEnhance.Contrast(image)
53
+ image = enhancer.enhance(1.5)
54
+
55
+ # Improve Sharpness
56
+ enhancer = ImageEnhance.Sharpness(image)
57
+ image = enhancer.enhance(1.5)
58
+
59
+ # Improve Brightness
60
+ enhancer = ImageEnhance.Brightness(image)
61
+ image = enhancer.enhance(1.2)
62
+
63
+ # Apply a filter to soften the image a little.
64
+ image = image.filter(ImageFilter.SMOOTH)
65
+
66
+ return image
67
+ except Exception as e:
68
+ logger.error(f"Error in image preprocessing: {str(e)}", exc_info=True)
69
+ raise
70
+
71
+ def process_image(self, image_stream):
72
+ try:
73
+ logger.info("Starting image processing")
74
+
75
+ # Save image temporarily because the model requires a file path.
76
+ temp_image_path = "temp_image.jpg"
77
+
78
+ # Reset the start pointer for BytesIO
79
+ image_stream.seek(0)
80
+
81
+ # Open and save the image temporarily.
82
+ image = Image.open(image_stream).convert('RGB')
83
+ processed_image = self.preprocess_image(image)
84
+ processed_image.save(temp_image_path)
85
+
86
+ # ocr
87
+ try:
88
+ result = self.model.chat(self.tokenizer, temp_image_path, ocr_type='format')
89
+ logger.info(f"Successfully extracted text: {result[:100]}...")
90
+
91
+ # Delete temporary file
92
+ if os.path.exists(temp_image_path):
93
+ os.remove(temp_image_path)
94
+
95
+ return result.strip()
96
+
97
+ except Exception as e:
98
+ logger.error(f"Error in OCR processing: {str(e)}", exc_info=True)
99
+ if os.path.exists(temp_image_path):
100
+ os.remove(temp_image_path)
101
+ raise
102
+
103
+ except Exception as e:
104
+ logger.error(f"Error in image processing: {str(e)}", exc_info=True)
105
+ return f"Error processing image: {str(e)}"
docker-compose.yml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.10'
2
+
3
+ services:
4
+ app:
5
+ build: .
6
+ ports:
7
+ - "7860:7860"
8
+ volumes:
9
+ - huggingface_cache:/root/.cache/huggingface
10
+ environment:
11
+ - MODEL_PATH=RufusRubin777/GOT-OCR2_0_CPU
12
+ restart: unless-stopped
13
+ deploy:
14
+ resources:
15
+ limits:
16
+ memory: 4G
17
+ reservations:
18
+ memory: 2G
19
+
20
+ volumes:
21
+ huggingface_cache:
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ accelerate==1.1.1
2
+ Flask==3.1.0
3
+ torch==2.5.1
4
+ torchvision==0.20.1
5
+ transformers==4.37.2
6
+ tiktoken==0.6.0
7
+ verovio==4.3.1
8
+ gunicorn