I’m new using label studio and I’m assigned to make a project for extracting data from invoices with different layouts using LayoutLM. Since the invoices have thousands of words and need to label in the BIO format, meaning I need to label all the words I wanted to speed up the process. I created a project on label studio Community with a storage to provide the images and deployed a backend ML to make the OCR and to label every word with O and then I will correct the entities I want to extract. I’m having an issue that is strange. I call the predict from label studio and there is a 200 from the backend side and also a predict associated on a tab but the ocr_text is not passing to the frontend and also the labeling is not showing. Here is my backend code where the answer is being made to the request. import pytesseract import json from PIL import Image import os from flask import Flask, request, jsonify class OCRNERModel: def setup(self): """Required setup endpoint for Label Studio""" print("ML Backend is set up successfully.") return jsonify({"status": "ok"}) def predict(self, request_data, **kwargs): tasks = request_data.get("tasks", []) if not tasks: return jsonify({"error": "No tasks found in request."}), 400 task = tasks[0] image_path = task["data"].get("image") # Convert relative URL to local path if image_path and image_path.startswith("/data/local-files/?d="): image_path = image_path.replace("/data/local-files/?d=", "/") if not image_path or not os.path.exists(image_path): return jsonify({"error": f"Image file not found: {image_path}"}), 404 try: img = Image.open(image_path) ocr_data = pytesseract.image_to_data(img, output_type=pytesseract.Output.DICT) ocr_text = pytesseract.image_to_string(img).strip() except Exception as e: return jsonify({"error": f"OCR extraction failed: {str(e)}"}), 500 predictions = [] id_counter = 1 img_width, img_height = img.size for i in range(len(ocr_data['text'])): text = ocr_data['text'].strip() if text: x_pixel = ocr_data['left'] y_pixel = ocr_data['top'] w_pixel = ocr_data['width'] h_pixel = ocr_data['height'] x_normalized = (x_pixel / img_width) * 100 y_normalized = (y_pixel / img_height) * 100 w_normalized = (w_pixel / img_width) * 100 h_normalized = (h_pixel / img_height) * 100 region_id = str(id_counter) predictions.append({ "id": region_id, "type": "rectanglelabels", "from_name": "label", "to_name": "image", "value": { "x": x_normalized, "y": y_normalized, "width": w_normalized, "height": h_normalized, "rotation": 0, "rectanglelabels": ["O"], }, }) predictions.append({ "id": f"T{region_id}", "type": "textarea", "from_name": "transcription", "to_name": "image", "value": { "text": [text], # Must be a list } }) id_counter += 1 response = { "data": { "image": task["data"].get("image"), "ocr_text": ocr_text }, "results": [ { "model_version": "ocr-bbox-v1", "result": predictions } ] } print("Sending Response:", json.dumps(response, indent=2)) return jsonify(response) # Initialize Flask app and model instance app = Flask(__name__) model = OCRNERModel() # Setup endpoint to configure the model backend @app.route('/setup', methods=['POST']) def setup(): """ Required setup endpoint for Label Studio """ return jsonify({"status": "ok"}) # Predict endpoint that Label Studio will call @app.route('/predict', methods=['POST']) def predict(): data = request.get_json() print(f"Received data: {json.dumps(data, indent=2)}") return model.predict(data) # Directly call model.predict and return its jsonify result # Health check endpoint for Label Studio to verify the backend is up @app.route('/health', methods=['GET']) def health(): """ Health check endpoint required by Label Studio """ return jsonify({"status": "ok"}) # Run the Flask application if __name__ == "__main__": app.run(host="0.0.0.0", port=8081) Here is my annotation config: <View> <View style="display:flex;align-items:start;gap:8px;flex-direction:row"><Image name="image" value="$image" zoom="true" zoomControl="true" rotateControl="true" width="100%" height="100%" maxHeight="auto" maxWidth="auto"/><View><Filter toName="label" minlength="0" name="filter"/><RectangleLabels name="label" toName="image" strokeWidth="1" showInline="false"> <Label value="B-NIF" background="#ff5733"/> <Label value="B-FORN" background="#33ff57"/> <Label value="B-DATA" background="#3375ff"/> <Label value="B-VALOR" background="#ff33a1"/> <Label value="B-NUMFAT" background="#f3ff33"/> <Label value="I-NIF" background="#ff5733"/> <Label value="I-FORN" background="#33ff57"/> <Label value="I-DATA" background="#3375ff"/> <Label value="I-VALOR" background="#ff33a1"/> <Label value="I-NUMFAT" background="#f3ff33"/> <Label value="O" background="#aaa"/> </RectangleLabels></View></View> <TextArea name="transcription" toName="image" editable="true" perRegion="true" required="false" rows="3" placeholder="Recognized Text" displayMode="region-list"/> </View> I was looking at some examples and also the documentation and anything works. Is there anything that I'm missing here? Thank you Continue reading...