import base64 import cv2 import numpy as np import uvicorn from fastapi import FastAPI, HTTPException from pydantic import BaseModel from collections import defaultdict app = FastAPI() @app.get("/") def root(): return { "status": "ok", "service": "iconCaptcha solver", "endpoint": "/solve" } class Input(BaseModel): image_base64: str def preprocess_image_memory(base64_str): try: img_data = base64.b64decode(base64_str) nparr = np.frombuffer(img_data, np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED) if img is None: raise ValueError("Invalid/corrupt image") if len(img.shape) == 3 and img.shape[2] == 4: alpha = img[:, :, 3] / 255.0 rgb = img[:, :, :3] white_bg = np.ones_like(rgb, dtype=np.uint8) * 255 img = (rgb * alpha[:, :, None] + white_bg * (1 - alpha[:, :, None])).astype(np.uint8) return img except Exception as e: raise ValueError(f"Error decoding image: {str(e)}") def extract_icon_positions(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) icons, pos = [], [] for c in contours: x, y, w, h = cv2.boundingRect(c) if w > 10 and h > 10: roi = cv2.resize(thresh[y:y+h, x:x+w], (50, 50)) icons.append(roi) pos.append((x, y)) return icons, pos def img_hash(img): img = cv2.resize(img, (8, 8)) return (img > img.mean()).astype(np.uint8).flatten() def find_rarest(icon_features, positions): if not icon_features: return None, None hashes = [img_hash(i) for i in icon_features] groups = defaultdict(list) for i, h in enumerate(hashes): found = False for label, group in groups.items(): if np.sum(h != hashes[group[0]]) < 3: group.append(i) found = True break if not found: groups[len(groups)] = [i] if not groups: return None, None idx = min(groups.values(), key=len)[0] return positions[idx] @app.post("/solve") def solve(data: Input): try: img = preprocess_image_memory(data.image_base64) icons, pos = extract_icon_positions(img) if not icons: return {"error": "No icons found", "x": 0, "y": 0} x, y = find_rarest(icons, pos) return {"x": int(x), "y": int(y)} except Exception as e: return {"error": str(e)} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=7860)