opencv
자동차 번호판 추출하기
꼰대코더
2025. 2. 15. 22:04
아이디어
1. 흑백으로 변환하여 contour 를 실시
2. approxPolyDP로 contour 결과로 부터 꼭지점이 4개(4개의 코너)인 것 중 가장 면적이 넓은 contour를 기억
approxPolyDP 알고리즘은 정확한 형태를 추출하는게 아니라 contours를 간단화 하는 것이다. contour의 형태에 적게 공헌하는 포인트들은 제거되고 형태에 공헌을 많이 하는 코너들은 남게 되는 것이다. 옵션의 epsilon 은 작을수록 제거되는 포인트들은 줄게되고 큰값 일수록 많은 포인트들이 제거되어 간단화가 크게 이루워 진다. |
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결과의 포인트들로 부터 대충의 형태를 추출 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() |