Загрузка данных
import cv2
import easyocr
import numpy as np
import time
import threading
from PIL import Image, ImageDraw, ImageFont
CAMERA_INDEX = 0
FRAME_WIDTH = 1280
FRAME_HEIGHT = 720
USE_GPU = False
PROCESS_EVERY_N = 2
CONF_THRESHOLD = 0.3
LANGUAGES = ['ru', 'en']
ROI = None
BOX_COLOR = (0, 255, 0)
TEXT_BG_COLOR = (0, 0, 0)
TEXT_COLOR = (0, 255, 255)
INDICATOR_COLOR = (0, 200, 80)
def load_font(size=22):
candidates = [
"C:/Windows/Fonts/arial.ttf",
"C:/Windows/Fonts/segoeui.ttf",
"C:/Windows/Fonts/tahoma.ttf",
]
for path in candidates:
try:
return ImageFont.truetype(path, size)
except Exception:
continue
return ImageFont.load_default()
def draw_text_pil(frame_bgr, text, pos, font):
img_pil = Image.fromarray(cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img_pil)
x, y = pos
bbox = draw.textbbox((x, y), text, font=font)
pad = 3
draw.rectangle((bbox[0] - pad, bbox[1] - pad, bbox[2] + pad, bbox[3] + pad), fill=TEXT_BG_COLOR)
draw.text((x, y), text, font=font, fill=TEXT_COLOR)
return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
def preprocess_frame(frame):
img = frame.copy()
if ROI:
x, y, w, h = ROI
img = img[y:y+h, x:x+w]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
denoised = cv2.bilateralFilter(gray, d=5, sigmaColor=35, sigmaSpace=35)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
enhanced = clahe.apply(denoised)
upscaled = cv2.resize(enhanced, None, fx=2.0, fy=2.0, interpolation=cv2.INTER_CUBIC)
binary = cv2.adaptiveThreshold(upscaled, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, 8)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
return cv2.cvtColor(closed, cv2.COLOR_GRAY2BGR)
def draw_results(frame, results, font):
overlay = frame.copy()
offset_x = ROI[0] if ROI else 0
offset_y = ROI[1] if ROI else 0
for (bbox, text, conf) in results:
if conf < CONF_THRESHOLD:
continue
pts = np.array(bbox, dtype=np.float32)
pts /= 2.0
pts[:, 0] += offset_x
pts[:, 1] += offset_y
pts = pts.astype(np.int32)
cv2.polylines(overlay, [pts], isClosed=True, color=BOX_COLOR, thickness=2)
tx = pts[0][0]
ty = pts[0][1] - 28
if ty < 5:
ty = pts[2][1] + 5
overlay = draw_text_pil(overlay, f"{text} ({conf:.2f})", (tx, ty), font)
return overlay
def draw_indicator(frame):
h, w = frame.shape[:2]
cx = w - 32
cy = 32
bgr = (INDICATOR_COLOR[2], INDICATOR_COLOR[1], INDICATOR_COLOR[0])
cv2.circle(frame, (cx, cy), 14, bgr, -1, cv2.LINE_AA)
cv2.circle(frame, (cx, cy), 14, (255, 255, 255), 1, cv2.LINE_AA)
class AsyncOCR:
def __init__(self, reader):
self.reader = reader
self.results = []
self.busy = False
self._lock = threading.Lock()
def process(self, img):
if self.busy:
return
self.busy = True
threading.Thread(target=self._run, args=(img,), daemon=True).start()
def _run(self, img):
try:
res = self.reader.readtext(
img,
detail=1,
paragraph=False,
batch_size=1,
contrast_ths=0.2,
adjust_contrast=0.7,
text_threshold=0.5,
low_text=0.3,
link_threshold=0.3,
canvas_size=1280,
mag_ratio=1.0,
)
with self._lock:
self.results = res
finally:
self.busy = False
def get_results(self):
with self._lock:
return list(self.results)
def main():
print("Загружаем EasyOCR...")
reader = easyocr.Reader(LANGUAGES, gpu=USE_GPU, verbose=False)
font = load_font(size=22)
cap = cv2.VideoCapture(CAMERA_INDEX)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, FRAME_WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
if not cap.isOpened():
print(f"Не могу открыть камеру {CAMERA_INDEX}")
return
ocr = AsyncOCR(reader)
frame_idx = 0
print("Q — выход, S — скриншот")
while True:
ret, frame = cap.read()
if not ret:
break
frame_idx += 1
if frame_idx % PROCESS_EVERY_N == 0:
ocr.process(preprocess_frame(frame))
display = draw_results(frame, ocr.get_results(), font)
if ROI:
x, y, w, h = ROI
cv2.rectangle(display, (x, y), (x+w, y+h), (255, 100, 0), 1)
draw_indicator(display)
cv2.imshow("Cable OCR", display)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('s'):
fname = f"screenshot_{int(time.time())}.jpg"
cv2.imwrite(fname, display)
print(f"Сохранено: {fname}")
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()