비전에이전트-포스터 인식

TechN0·2025년 5월 9일
  • 초기 코드

https://github.com/techn-0/computer_vision_STD/blob/main/6-4초기 코드.py

문제 발생

문제해결
등록 포스터 이미지 사이즈 불일치cv.resize()를 사용하여 등록 포스터를 (240×320) 크기로 통일
매칭 수 높은 포스터 하나만 결과 출력루프로 순회하여 모든 포스터 인식
부정확한 매칭 발생, 필터링 없음T값을 0.4로 낮춰 부정확한 매칭 필터링, 좋은 매칭을 상위 30개로 제한, Homography 결과가 실패 또는 inlier 10개 미만이면 무시
import cv2 as cv
import numpy as np
from PyQt5.QtWidgets import *
import sys
import winsound

class TrafficWeak(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('영화찾기')
        self.setGeometry(300, 300, 800, 300)

        # 버튼 및 라벨 생성
        signButton = QPushButton('포스터 등록', self)
        roadButton = QPushButton('타겟 불러오기', self)
        recognitionButton = QPushButton('인식', self)
        quitButton = QPushButton('종료', self)
        self.label = QLabel('welcome', self)

        # 버튼 및 라벨 위치 설정
        signButton.setGeometry(10, 10, 100, 30)
        roadButton.setGeometry(110, 10, 100, 30)
        recognitionButton.setGeometry(210, 10, 100, 30)
        quitButton.setGeometry(510, 10, 100, 30)
        self.label.setGeometry(10, 50, 780, 250)

        # 버튼 클릭 이벤트 연결
        signButton.clicked.connect(self.signFunction)
        roadButton.clicked.connect(self.roadFunction)
        recognitionButton.clicked.connect(self.recognitionFunction)
        quitButton.clicked.connect(self.quitFunction)

        # 포스터 파일 경로 및 이름
        self.signFiles = [['토토로.jpg', '토토로'], ['로보캅.jpg', '로보캅'], ['메트로폴리스.jpg', '메트로폴리스']]
        self.signImgs = []

    def signFunction(self):
        # 포스터 등록 함수
        self.label.clear()
        self.label.setText('포스터 등록')

        standard_size = (240, 320)  # 표준 포스터 크기

        for fname, _ in self.signFiles:
            img = cv.imread(fname)
            resized = cv.resize(img, standard_size)  # 크기 통일
            self.signImgs.append(resized)
            cv.imshow(fname, resized)

    def roadFunction(self):
        # 영화관 장면 이미지 불러오기
        if self.signImgs == []:
            self.label.setText('먼저 포스터 등록!')
        else:
            fname = QFileDialog.getOpenFileName(self, 'Open file', './')
            self.roadImg = cv.imread(fname[0])
            if self.roadImg is None:
                sys.exit('파일 찾을 수 없음')
            cv.imshow('Road scene', self.roadImg)

    def recognitionFunction(self):
        # 포스터 인식 함수
        if self.roadImg is None:
            self.label.setText('타겟 입력!')
            return

        sift = cv.SIFT_create()  # SIFT 객체 생성

        KD = []  # 등록 포스터들의 키포인트와 디스크립터 저장
        for img in self.signImgs:
            gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
            KD.append(sift.detectAndCompute(gray, None))

        # 장면 이미지의 키포인트 및 디스크립터 추출
        grayRoad = cv.cvtColor(self.roadImg, cv.COLOR_BGR2GRAY)
        road_kp, road_des = sift.detectAndCompute(grayRoad, None)

        matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED)

        display_img = self.roadImg.copy()  # 원본 훼손 방지용 복사본

        for i in range(len(KD)):
            sign_kp, sign_des = KD[i]
            knn_matches = matcher.knnMatch(sign_des, road_des, 2)

            # Lowe's ratio test 적용
            good_match = []
            for m, n in knn_matches:
                if m.distance < 0.4 * n.distance:
                    good_match.append(m)

            if len(good_match) < 10:
                continue  # 좋은 매칭이 충분하지 않으면 넘어감

            # 상위 30개 좋은 매칭만 사용
            good_match = sorted(good_match, key=lambda x: x.distance)[:30]

            # 매칭된 포인트 좌표 추출
            points1 = np.float32([sign_kp[m.queryIdx].pt for m in good_match])
            points2 = np.float32([road_kp[m.trainIdx].pt for m in good_match])

            # Homography 계산
            H, mask = cv.findHomography(points1, points2, cv.RANSAC, 3.0)
            if H is None or mask is None or np.sum(mask) < 10:
                continue  # Homography 실패나 신뢰성 낮으면 무시

            # 포스터 외곽 박스 변환
            h1, w1 = self.signImgs[i].shape[:2]
            box = np.float32([[0, 0], [0, h1], [w1, h1], [w1, 0]]).reshape(-1, 1, 2)
            box_transformed = cv.perspectiveTransform(box, H)

            # 초록색 박스 그리기
            display_img = cv.polylines(display_img, [np.int32(box_transformed)], True, (0, 255, 0), 4)

            # 매칭 결과(drawMatches) 이미지 생성
            img_match = np.empty(
                (max(h1, display_img.shape[0]), w1 + display_img.shape[1], 3),
                dtype=np.uint8
            )

            cv.drawMatches(
                self.signImgs[i], sign_kp,
                display_img, road_kp,
                good_match, img_match,
                flags=cv.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS
            )

            cv.imshow(f"Matches: {self.signFiles[i][1]}", img_match)

            # 인식 성공 표시 및 소리 알림
            self.label.setText(self.signFiles[i][1] + ' 영화 감지됨!')
            winsound.Beep(2000, 300)

    def quitFunction(self):
        # 창 종료 함수
        cv.destroyAllWindows()
        self.close()

# 프로그램 실행
app = QApplication(sys.argv)
window = TrafficWeak()
window.show()
app.exec_()

0개의 댓글