본문 바로가기
opencv

자동차 번호판 추출하기

by 꼰대코더 2025. 2. 15.

 

아이디어

1. 흑백으로 변환하여 contour 를 실시

2. approxPolyDP로 contour 결과로 부터 꼭지점이 4개(4개의 코너)인 것 중 가장 면적이 넓은 contour를 기억

3. boundingRect 로 사각형의 좌표와 사이즈를 추출

 

주의)

노이즈가 심한 경우 contour결과에 영향을 줄 수 있으니 필터링에 신경을 더 써야 할 경우가 있다.

번호판 보다 더 큰 contour가 있을 수 있으니 고정 이미지라면 정해진 사이즈에 근접한 contour 를 선택하면 된다.

cv::Mat img = cv::imread("d:\\plate.jpg");
# gray 변환
cv::Mat gray;
cv::cvtColor(img, gray, CV_BGR2GRAY);
# 노이즈를 완화해 주자
cv::GaussianBlur(gray, gray, cv::Size(3, 3), 0.0);
# 흑백이미지 변환 -> 사진이면 빛의 영향으로 밝기가 고르지 못하니 11x11 내의 이진화 방법을 사용
cv::Mat thresh;
cv::adaptiveThreshold(gray, thresh, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 11, 2);

# contour 실시 -> https://eldercoder.tistory.com/63 참조

std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
# 번호판외부에도 닫혀진 contour가 있을 수 있으니 이번엔 모든 contour를 뽑아내자 ( CV_RETR_LIST )
cv::findContours(thresh, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

# 최대 면적을 같은 contour index 를 기억하기 위한 변수
double maxArea = 0.0;
size_t maxAreaIdx = 0;

for (size_t i = 0; i < contours.size(); i++) {
        std::vector<std::vector<cv::Point>> points;
        # contour결과의 포인트들로 부터 대충의 형태를 추출(조건은 1% 이상 변형되지 않을 것)
        cv::approxPolyDP(cv::Mat(contours[i]), points, 0.01 * cv::arcLength(contours[i], true), true);
        # 꼭지점이 4개

        if (points.size() == 4) {
            double area = cv::contourArea(contours[i]);
            if (area > maxArea  ) {
                maxArea   = area;
                maxAreaIdx  = i;
            }
        }
}

#  해당 contour 로 부터 좌표와 사이즈 추출
cv::Rect rect = cv::boundingRect(contours[ maxAreaIdx]);
cv::rectangle(img, rect, cv::Scalar(0, 0, 255), 3);

cv::imwrite("d:\\result.png", img);

return 0;

 

Python

import numpy as np
import cv2
import pytesseract
import matplotlib.pyplot as plt

img = cv2.imread('/home/muthu/Documents/3r9OQ.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY,11,2)

contours,h = cv2.findContours(thresh,1,2)

largest_rectangle = [0,0]
for cnt in contours:
    approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True)
    if len(approx)==4: 
        area = cv2.contourArea(cnt)
        if area > largest_rectangle[0]:
            largest_rectangle = [cv2.contourArea(cnt), cnt, approx]

x,y,w,h = cv2.boundingRect(largest_rectangle[1])
roi=img[y:y+h,x:x+w]

plt.imshow(roi, cmap = 'gray')
plt.show()