# data[].attributes.body
ĐẶNG THỊ THANH HUYỀN

NGUYỄN QUỐC TÍN

NGUYỄN VĂN BẢO CƯỜNG

NGUYỄN NHƯ CƯỜNG

ĐINH BẠT SƠN

# data[].attributes.headline
Chương trình thực hiện nhận diện biển số xe

# data[].attributes.subject
Chương trình thực hiện nhận diện biển số xe

# data[].attributes.attachment_link.readable_text
import cv2
import numpy as np
import pytesseract
import os
from datetime import datetime

pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'

def preprocess_frame(frame):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gray_blur = cv2.GaussianBlur(gray, (5, 5), 0)
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
    enhanced = clahe.apply(gray_blur)
    edges = cv2.Canny(enhanced, 100, 200)
    return edges, enhanced

def find_license_plate(contours, min_area=500):
    potential_plates = []
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area < min_area:
            continue
        peri = cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
        if len(approx) == 4:
            x, y, w, h = cv2.boundingRect(approx)
            aspect_ratio = float(w) / h
            if 1.4 <= aspect_ratio <= 4.4:
                potential_plates.append([area, approx])
    if potential_plates:
        return max(potential_plates, key=lambda x: x[0])
    return [0, None]

def segment_and_recognize_chars(plate_img):
    if plate_img is None or plate_img.size == 0:
        return "", None

    gray = cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (3, 3), 0)
    binary = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                   cv2.THRESH_BINARY_INV, 19, 9)

    contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    char_boxes = []
    height, width = plate_img.shape[:2]
    min_char_width = width * 0.03
    min_char_height = height * 0.35
    max_char_width = width * 0.15

    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        if (min_char_width < w < max_char_width and
                min_char_height < h and
                0.2 < float(w) / h < 1.1):
            pad = 3
            x_start = max(0, x - pad)
            y_start = max(0, y - pad)
            x_end = min(width, x + w + pad)
            y_end = min(height, y + h + pad)
            char_boxes.append([x_start, y_start, x_end, y_end])

    mid_y = height // 2
    top_chars = sorted([box for box in char_boxes if box[1] < mid_y], key=lambda x: x[0])
    bottom_chars = sorted([box for box in char_boxes if box[1] >= mid_y], key=lambda x: x[0])

    result = plate_img.copy()
    config = r'--oem 3 --psm 6 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

    top_text = ""
    for x1, y1, x2, y2 in top_chars:
        char_roi = binary[y1:y2, x1:x2]
        if char_roi.size == 0:
            continue
        char_roi = cv2.resize(char_roi, (30, 60))
        char_roi = cv2.copyMakeBorder(char_roi, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=[0])
        char = pytesseract.image_to_string(char_roi, config=config).strip()
        if char and len(char) == 1:
            cv2.rectangle(result, (x1, y1), (x2, y2), (0, 255, 0), 1)
            text_x = x1 + (x2 - x1) // 2  # Center the text horizontally
            text_y = y2 - 1  # Move text below the bounding box
            cv2.putText(result, char, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
            top_text += char

    bottom_text = ""
    for x1, y1, x2, y2 in bottom_chars:
        char_roi = binary[y1:y2, x1:x2]
        if char_roi.size == 0:
            continue
        char_roi = cv2.resize(char_roi, (30, 60))
        char_roi = cv2.copyMakeBorder(char_roi, 10, 10, 10, 10, cv2.BORDER_CONSTANT, value=[0])
        char = pytesseract.image_to_string(char_roi, config=config).strip()
        if char and len(char) == 1:
            cv2.rectangle(result, (x1, y1), (x2, y2), (0, 255, 0), 1)
            text_x = x1 + (x2 - x1) // 2  # Center the text horizontally
            text_y = y2 - 5  # Move text below the bounding box
            cv2.putText(result, char, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
            bottom_text += char

    formatted_text = f"{top_text}{bottom_text}"
    for boxes in [top_chars, bottom_chars]:
        for x1, y1, x2, y2 in boxes:
            cv2.rectangle(result, (x1, y1), (x2, y2), (0, 255, 0), 2)
    print(f"\nLicense Plate: {formatted_text}")
    save_to_file(formatted_text)
    return result

def save_to_file(text, file_name="E:\\HTVT\\bienso.txt"):
    with open(file_name, "a") as file:
        file.write(f"{datetime.now()}: {text}\n")

video = cv2.VideoCapture(0)
video.set(cv2.CAP_PROP_FRAME_WIDTH, 1000)
video.set(cv2.CAP_PROP_FRAME_HEIGHT, 600)
while True:
    ret, frame = video.read()
    if not ret:
        break
    edges, enhanced = preprocess_frame(frame)
    cv2.imshow('Edges', edges)

    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    largest_rectangle = find_license_plate(contours)

    if largest_rectangle[0] > 0:
        x, y, w, h = cv2.boundingRect(largest_rectangle[1])
        plate_img = frame[y:y + h, x:x + w]

        if plate_img.size > 0:
            result = segment_and_recognize_chars(plate_img)
            if result is not None:
                cv2.drawContours(frame, [largest_rectangle[1]], 0, (0, 255, 0), 1)
                cv2.imshow('License Plate', result)

    cv2.imshow('License Plate Detection', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video.release()
cv2.destroyAllWindows()
