⚡ MareArts ANPR V14 - Advanced Manual Processing
Ready to take control? In this advanced guide, I'll show you how to manually process detections, measure performance, and optimize for your specific use case.
🎯 Why Manual Processing?
- Full control over detection pipeline
- Custom filtering and post-processing
- Performance measurement and optimization
- Integration with existing computer vision pipelines
- Custom confidence thresholds per stage
🔧 Manual Detection & OCR Pipeline
from marearts_anpr import ma_anpr_detector_v14, ma_anpr_ocr_v14
import cv2
from PIL import Image
import time
# Initialize models
detector = ma_anpr_detector_v14(
"medium_640p_fp32",
user_name, serial_key, signature,
backend="cpu",
conf_thres=0.25,
iou_thres=0.5
)
ocr = ma_anpr_ocr_v14("medium_fp32", "eup", user_name, serial_key, signature)
# Load image
img = cv2.imread("plate.jpg")
# Step 1: Detect license plates
start = time.time()
detections = detector.detector(img)
detection_time = time.time() - start
print(f"Detection time: {detection_time:.4f}s")
print(f"Found {len(detections)} plate(s)")
# Step 2: Process each detection
results = []
ocr_time = 0
for i, box_info in enumerate(detections):
# Get bounding box
bbox = box_info['bbox'] # [x1, y1, x2, y2]
score = box_info['score'] # Detection confidence
# Crop plate region
x1, y1, x2, y2 = int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3])
crop = img[y1:y2, x1:x2]
if crop.size == 0:
continue
# Convert to PIL for OCR
pil_img = Image.fromarray(crop)
if pil_img.mode != "RGB":
pil_img = pil_img.convert("RGB")
# Run OCR
start = time.time()
text, confidence = ocr.predict(pil_img)
elapsed = time.time() - start
ocr_time += elapsed
print(f"Plate {i+1}: {text} ({confidence}%) - {elapsed:.4f}s")
results.append({
"ocr": text,
"ocr_conf": confidence,
"bbox": [x1, y1, x2, y2],
"det_conf": int(score * 100)
})
print(f"\nTotal time: {detection_time + ocr_time:.4f}s")
📊 Detection Object Structure
# detector.detector(img) returns list of dictionaries:
[
{
'bbox': [x1, y1, x2, y2], # Bounding box coordinates
'score': 0.95, # Detection confidence (0-1)
'class': 'license_plate' # Object class
},
...
]
# ocr.predict(pil_image) returns tuple:
("ABC1234", 98.5) # (text, confidence_percentage)
🚀 Backend Performance Comparison
backends = ["cpu", "cuda"] # Add "directml" on Windows
for backend_name in backends:
try:
print(f"\n🔧 Testing {backend_name}...")
# Initialize with specific backend
test_detector = ma_anpr_detector_v14(
"medium_640p_fp32",
user_name, serial_key, signature,
backend=backend_name,
conf_thres=0.25
)
# Measure performance
start = time.time()
detections = test_detector.detector(img)
elapsed = time.time() - start
print(f"Detected {len(detections)} plates in {elapsed:.4f}s")
print(f"Speed: {1/elapsed:.1f} FPS")
except Exception as e:
print(f"⚠️ {backend_name} not available: {e}")
⚙️ Performance Results (Typical)
| Backend |
Detection |
OCR |
Total |
FPS |
| CPU (i7) |
~0.15s |
~0.03s |
~0.18s |
~5.5 |
| CUDA (RTX 3060) |
~0.008s |
~0.002s |
~0.01s |
~100 |
Result: GPU acceleration = 18x faster! 🚀
🎛️ Custom Filtering
# Filter detections by confidence
min_detection_conf = 0.50
min_ocr_conf = 80.0
filtered_results = []
for box_info in detections:
if box_info['score'] < min_detection_conf:
continue # Skip low confidence detections
# Process with OCR...
text, conf = ocr.predict(plate_crop)
if conf < min_ocr_conf:
continue # Skip low confidence OCR
filtered_results.append({
"text": text,
"confidence": conf,
"bbox": bbox
})
print(f"After filtering: {len(filtered_results)} high-confidence plates")
🎨 Custom Visualization
import cv2
# Draw boxes and text on image
for result in results:
x1, y1, x2, y2 = result['bbox']
text = result['ocr']
conf = result['ocr_conf']
# Draw rectangle
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# Draw text
label = f"{text} ({conf}%)"
cv2.putText(img, label, (x1, y1-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imwrite("result.jpg", img)
📹 Video Processing Pipeline
import cv2
# Open video
cap = cv2.VideoCapture("traffic.mp4")
frame_count = 0
plate_history = {}
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
frame_count += 1
# Process every N frames (skip frames for speed)
if frame_count % 5 != 0:
continue
# Detect plates
detections = detector.detector(frame)
for det in detections:
bbox = det['bbox']
x1, y1, x2, y2 = int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3])
crop = frame[y1:y2, x1:x2]
if crop.size == 0:
continue
# OCR
pil_crop = Image.fromarray(cv2.cvtColor(crop, cv2.COLOR_BGR2RGB))
text, conf = ocr.predict(pil_crop)
# Track plates (simple tracking by position)
plate_id = f"{x1//50}_{y1//50}"
if plate_id not in plate_history:
plate_history[plate_id] = []
plate_history[plate_id].append(text)
# Draw
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(frame, text, (x1, y1-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
cv2.imshow('ANPR', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
# Print detected plates
print("\nDetected plates:")
for plate_id, texts in plate_history.items():
# Most common text for this plate
most_common = max(set(texts), key=texts.count)
print(f" {most_common} (seen {len(texts)} times)")
💾 Batch Processing from Directory
import os
from pathlib import Path
image_dir = Path("./images")
results_all = {}
for img_path in image_dir.glob("*.jpg"):
print(f"Processing {img_path.name}...")
img = cv2.imread(str(img_path))
detections = detector.detector(img)
plates = []
for det in detections:
bbox = det['bbox']
x1, y1, x2, y2 = int(bbox[0]), int(bbox[1]), int(bbox[2]), int(bbox[3])
crop = img[y1:y2, x1:x2]
if crop.size > 0:
pil_crop = Image.fromarray(cv2.cvtColor(crop, cv2.COLOR_BGR2RGB))
text, conf = ocr.predict(pil_crop)
plates.append({"text": text, "conf": conf})
results_all[img_path.name] = plates
# Save results
import json
with open("results.json", "w") as f:
json.dump(results_all, f, indent=2)
print(f"\nProcessed {len(results_all)} images")
🎓 Advanced Tips
- GPU Memory: Use
cuda backend for 10-100x speedup
- Confidence Tuning: Lower
conf_thres to 0.15-0.20 for difficult images
- IOU Threshold: Increase
iou_thres to reduce duplicate detections
- Batch Processing: Process multiple crops at once with
ocr.predict([img1, img2, ...])
- Frame Skipping: Process every Nth frame in videos for speed
- Multi-threading: Run detector and OCR in separate threads
🔍 Troubleshooting
No detections?
- Lower
conf_thres to 0.15
- Try larger model (
large_640p_fp32)
- Check image quality and resolution
Wrong OCR results?
- Verify correct region (
kr, eup, na, cn)
- Try larger OCR model (
large_fp32)
- Check plate crop quality
Slow performance?
- Use GPU backend (
cuda or directml)
- Use smaller models (
small_640p_fp32, small_fp32)
- Skip video frames
- Batch process multiple images
💡 Conclusion
Manual processing gives you complete control over the ANPR pipeline. Use it for:
- ✅ Custom filtering and validation
- ✅ Performance optimization
- ✅ Video stream processing
- ✅ Integration with existing CV pipelines
- ✅ Advanced visualization and tracking
Happy optimizing! ⚡🚗