AIoT

AIoT 정규 64일차

맥기짱짱 2024. 4. 11. 16:19

번호판 인식 프로젝트(完)

DB 조회 후 PASS 여부 출력

: 우선 DB(지금의 경우에는 MySQL)와 연결을 하기 위해서 "mysql.connector" 모듈을 설치해 준다.

pip install mysql.connector

 

그다음 DB연결을 위해 필요한 정보들을 변수를 지정해서 설정해 준다. (보안 문제로 별도에 폴더에서 사용하는 게 권장됨)

        connection = mysql.connector.connect(
        host='호스트명',
        user='유저이름',
        password='비밀번호',
        database='데이터베이스명',
        auth_plugin='mysql_native_password'

 

밑에 auth_plugin을 설정한 이유는  MySQL 데이터베이스에서 사용자 인증에 대한 플러그인을 설정하는 것이다. 주로 MySQL 서버에 연결하는 클라이언트가 비밀번호를 제공하여 인증하는 데 사용됩니다.

'mysql_native_password'는 MySQL의 기본 네이티브 비밀번호 인증 방법 중 하나이다. 이 방법은 비밀번호를 해싱하여 저장하고, 클라이언트가 인증 시에 제공한 비밀번호를 해시값과 비교하여 인증하는 방식이다.

 더 안전한 인증 방법으로 'caching_sha2_password'나 'sha256_password'와 같은 플러그인을 사용하는 것이 권장된다. 이러한 방법은 더 강력한 보안을 제공하며, 해시된 비밀번호를 더 안전하게 저장하고 전송하는 데 사용된다.

마지막으로 tkinter에 gui를 추가하면 완성이다.

license_plate_pass_or_fail = Label(root, font=('Arial', 25))
license_plate_pass_or_fail.pack()

 

 

최종. py

import cv2
import pytesseract
from tkinter import *
from tkinter import filedialog
from PIL import Image, ImageTk
import mysql.connector

# MySQL 연결 정보
mysql_config = {
    'host': '호스트명, 주로 localhost로 설정 되어있음',
    'user': '유저 이름',
    'password': '비밀번호',
    'database': '데이터베이스명'
}

pytesseract.pytesseract.tesseract_cmd = 'C:/Program Files/Tesseract-OCR/tesseract' # 테서렉트 파일위치

# 데이터 전처리
def preprocess_text(text):
    processed_text = ''.join(char for char in text if char.isdigit() or '가' <= char <= '힣')
    processed_text = processed_text[:8]
    return processed_text

# 이미지 가져오기
def load_image():
    global image_data
    path = filedialog.askopenfilename()
    if path:
        image_data = cv2.imread(path)
        image_data = cv2.cvtColor(image_data, cv2.COLOR_BGR2RGB)
        display_image()

# tkinter에서 이미지를 display하기 위한 함수
def display_image():
    global image_label, image_data, license_plate_label
    gray_image = cv2.cvtColor(image_data, cv2.COLOR_RGB2GRAY)
    canny_edge = cv2.Canny(gray_image, 170, 200)
    contours, new = cv2.findContours(canny_edge.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=cv2.contourArea, reverse=True)[:30]
    contour_with_license_plate = None
    license_plate = None
    x = None
    y = None
    w = None
    h = None
    for contour in contours:
        perimeter = cv2.arcLength(contour, True)
        approx = cv2.approxPolyDP(contour, 0.01 * perimeter, True)
        if len(approx) == 4:
            contour_with_license_plate = approx
            x, y, w, h = cv2.boundingRect(contour)
            license_plate = gray_image[y:y + h, x:x + w]
            break
    if license_plate is not None:
        (thresh, license_plate) = cv2.threshold(license_plate, 127, 255, cv2.THRESH_BINARY)
        license_plate = cv2.bilateralFilter(license_plate, 11, 17, 17)
        (thresh, license_plate) = cv2.threshold(license_plate, 150, 180, cv2.THRESH_BINARY)
        text = pytesseract.image_to_string(license_plate, lang='kor')
        text = preprocess_text(text)
        image_with_plate = cv2.rectangle(image_data.copy(), (x, y), (x + w, y + h), (0, 0, 255), 3)
        image_with_plate = cv2.putText(image_with_plate, text, (x - 100, y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
        image_with_plate = Image.fromarray(image_with_plate)
        image_with_plate = ImageTk.PhotoImage(image_with_plate)
        license_plate_label.config(image=image_with_plate)
        license_plate_label.image = image_with_plate
        license_plate_label.pack()
        license_plate_text.set("License Plate: " + text)
        pass_or_fail(text)
    else:
        license_plate_text.set("License Plate: Not Found")

# DB(MySQL) 조회 후 pass or fail 여부 출력 
def pass_or_fail(license_plate_text):
    try:
        # MySQL에 연결
        connection = mysql.connector.connect(
        host='호스트명',
        user='유저이름',
        password='비밀번호',
        database='데이터베이스명',
        auth_plugin='mysql_native_password'
    )

        # MySQL 쿼리 실행
        cursor = connection.cursor()

        if len(license_plate_text) >= 6:
            query = "SELECT * FROM Vehicle_num2 WHERE car_num LIKE CONCAT('%', %s, '%')"
            cursor.execute(query, (license_plate_text,))
            result = cursor.fetchone()
        else:
            # 데이터 길이가 6자 미만인 경우에 대한 처리
            result = None

        # pass면 초록색, fail이면 빨간색으로 출력
        if result:
            license_plate_pass_or_fail.config(text="PASS", fg="green")
        else:
            license_plate_pass_or_fail.config(text="FAIL", fg="red")

        connection.close()
    except mysql.connector.Error as err:
        print("MySQL Error: {}".format(err))

# Tkinter GUI 생성
root = Tk()
root.title("License Plate Recognition")

# 메뉴 생성
menu_bar = Menu(root)
file_menu = Menu(menu_bar, tearoff=0)
file_menu.add_command(label="Open", command=load_image)
file_menu.add_command(label="Exit", command=root.quit)
menu_bar.add_cascade(label="File", menu=file_menu)
root.config(menu=menu_bar)

# 이미지 프레임 생성
image_frame = Frame(root)
image_frame.pack()

# 이미지 표시 라벨 생성
image_label = Label(image_frame)
image_label.pack()

# 번호판 텍스트 라벨 생성
license_plate_label = Label(root)
license_plate_label.pack()

license_plate_text = StringVar()
license_plate_text_label = Label(root, textvariable=license_plate_text, font=('Arial', 25))
license_plate_text_label.pack()

# pass 여부 표시 라벨 생성
license_plate_pass_or_fail = Label(root, font=('Arial', 25))
license_plate_pass_or_fail.pack()

root.mainloop()

 

 

DB에 등록되어 있는 번호판
DB에 등록 안되어 있는 번호판
아무것도 인식하지 못했을때

 

이제 마지막으로 자동차 정보를 추가할 수 있게 기능을 더해보자.

def register_vehicle():
    register_window = Toplevel(root)
    register_window.title("Register Vehicle")

    def insert_data():
        try:
            # MySQL에 연결
            connection = mysql.connector.connect(
            host='호스트명',
            user='유저네임',
            password='비밀번호',
            database='DB이름',
            auth_plugin='mysql_native_password')

            # MySQL 쿼리 실행
            cursor = connection.cursor()

            owner_name = owner_name_entry.get()
            car_num = car_num_entry.get()

            query = "INSERT INTO Vehicle_num2 (car_id, car_owner, car_num) VALUES (NULL, %s, %s)"
            cursor.execute(query, (owner_name, car_num))
            connection.commit()

            messagebox.showinfo("Success", "Vehicle registered successfully!")

            connection.close()
        except mysql.connector.Error as err:
            print("MySQL Error: {}".format(err))
            messagebox.showerror("Error", "Failed to register vehicle!")

    owner_name_label = Label(register_window, text="Owner Name:")
    owner_name_label.grid(row=0, column=0, padx=10, pady=5)
    owner_name_entry = Entry(register_window)
    owner_name_entry.grid(row=0, column=1, padx=10, pady=5)

    car_num_label = Label(register_window, text="Car Number:")
    car_num_label.grid(row=1, column=0, padx=10, pady=5)
    car_num_entry = Entry(register_window)
    car_num_entry.grid(row=1, column=1, padx=10, pady=5)

    submit_button = Button(register_window, text="Submit", command=insert_data)
    submit_button.grid(row=2, columnspan=2, padx=10, pady=5)

: car_id는 AUTO_INCREMENT로 설정해 놓았기 때문에 따로 지정할 필요가 없다.

 

>> 그리고 마지막으로 DB 조회하는 기능까지 만들면 완성이다.

def show_registered_vehicles():
    try:
        # MySQL에 연결
        connection = mysql.connector.connect(
        host='호스트명',
        user='유저네임',
        password='비밀번호',
        database='DB이름',
        auth_plugin='mysql_native_password'
        )
        
        # MySQL 쿼리 실행
        cursor = connection.cursor()

        query = "SELECT * FROM Vehicle_num2"
        cursor.execute(query)
        results = cursor.fetchall()

        if results:
            for row in results:
                print(row)
        else:
            print("No registered vehicles found.")

        connection.close()
    except mysql.connector.Error as err:
        print("MySQL Error: {}".format(err))

 

- 메뉴 추가

register_menu = Menu(menu_bar, tearoff=0)
register_menu.add_command(label="Register Vehicle", command=register_vehicle)
register_menu.add_command(label="Show Registered Vehicles", command=show_registered_vehicles)
menu_bar.add_cascade(label="Register", menu=register_menu)

 

- 결과물

 

차량 등록창

 

Submit 버튼을 누르면 db에 전송된다.

 

db에 잘 들어간 모습~

 

 

등록된 차량이 print 된 모습

 

 

'AIoT' 카테고리의 다른 글

AIoT 정규 66일차  (0) 2024.04.17
AIoT 정규 65일차  (0) 2024.04.15
AIoT 정규 63일차  (0) 2024.04.09
AIoT 정규 62일차  (0) 2024.04.08
AIoT 정규 61일차  (0) 2024.04.05