OpenCV 자동차 번호판 포 지 셔 닝 실현(C++)

최근 에 C++를 접 하기 시 작 했 는데 OpenCV 프로젝트 를 가지 고 연습 을 합 니 다.자동차 번호판 자동 식별 시스템 에서 자동차 이미지 에서 자동차 번호판 문자 처 리 를 얻 는 것 은 복잡 한 과정 이 므 로 본 고 는 간단 한 방법 으로 자동차 번호판 의 포 지 셔 닝 을 처리 하고 자 한다.
중국의 자동차 번호판 은 보통 7 개의 문자 와 한 점 으로 구성 되 는데 자동차 번호판 문자 의 높이 와 너 비 는 각각 90mm 와 45mm 이 고 7 개의 문자 간 의 거리 도 고정 적 인 12mm 이 며 점 분할 문자 의 지름 은 10mm 이다.
사용 한 그림 은 바 이 두 에서 마음대로 찾 은 것 입 니 다.(삭제)원본 그림 과 그 레이스 케 일 그림 을 보 여 줍 니 다.

#include <iostream> 
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>

using namespace std;
using namespace cv;

int main() {
 //     
 Mat img = imread("license.jpg");
 Mat gray_img;
 //       
 cvtColor(img, gray_img, CV_BGR2GRAY);
 //           
 imshow("  ", img);
 imshow("   ", gray_img);
 waitKey(0);
 return 0;
}

그 레이스 케 일 이미지 의 모든 픽 셀 은 하나의 디지털 로 계량 화 되 고 컬러 이미지 의 모든 픽 셀 은 세 개의 숫자 로 구 성 된 벡터 로 계량 화 되 며 그 레이스 케 일 이미 지 를 사용 하면 후속 처리 가 더욱 편리 합 니 다.
영상 소음 감소
모든 이미 지 는 어느 정도 의 소음 을 포함 하고 대부분 상황 에서 부 드 러 운 기술(필터 또는 소음 감소 기술 이 라 고도 함)을 억제 하거나 제거 해 야 한다.이런 기술 은 2 차원 분 산 된 볼 륨 을 바탕 으로 하 는 고 스 부 드 러 움,평균 값 부 드 러 움,통계학 적 방법 을 바탕 으로 하 는 중간 값 부 드 러 움 등 을 포함한다.여 기 는 2 차원 분 산 된 볼 륨 을 바탕 으로 하 는 고 스 평활 으로 그 레이스 케 일 이미 지 를 소음 감소 처리 하고 처리 한 이미지 효 과 는 다음 과 같다.

형태학 적 처리
고 스 가 소음 을 제거 한 후에 뒤에 자동차 번호판 의 윤곽 을 더욱 정확하게 추출 하기 위해 우 리 는 이미지 에 대해 형태학 적 처 리 를 해 야 한다.여기 서 우 리 는 그것 에 대해 연산 을 하고 처리 한 후에 다음 과 같다.

연산 을 하 는 것 은 먼저 erode 를 한 다음 에 dilate 를 하 는 과정 이 바로 연산 을 하 는 것 이다.이것 은 밝기 가 비교적 높 은 작은 구역 을 제거 하고 섬세 한 점 에서 물 체 를 분리 하 며 비교적 큰 물체 에 대해 그 면적 을 뚜렷하게 바 꾸 지 않 은 상황 에서 그 경 계 를 부 드 럽 게 하 는 등 역할 을 한다.
erode 작업 은 부식 작업 입 니 다.볼 륨 과 유사 하고 이웃 도 메 인 연산 입 니 다.그러나 가중 구 합 이 아니 라 이웃 도 메 인 에 있 는 픽 셀 점 을 그 레이스 케 일 값 에 따라 정렬 한 다음 에 이 그룹의 최소 값 을 출력 그 레이스 케 일 값 으로 선택 합 니 다.
dilate 작업 은 팽창 작업 으로 부식 작업 과 유사 하 며 팽창 은 모든 위치 이웃 지역 내의 최대 치 를 취한 다.이웃 지역 내의 최대 치 를 취 하 는 것 이 므 로 팽창 한 출력 이미지 의 전체적인 밝기 의 평균 치 는 원래 그림 보다 상승 하고 이미지 에서 밝 은 물체 의 크기 는 커진다.반면 어두 운 물체 의 사 이 즈 는 줄 어 들 고 사라 진다.
한도 값 분할
초보적인 형태학 처 리 를 완성 한 후에 우 리 는 이미지 에 대해 한도 값 분할 을 해 야 한다.우 리 는 여기 서 Otsu 한도 값 처 리 를 사 용 했 고 처리 후의 효 과 는 다음 과 같다.

이미지 에 대해 디지털 처 리 를 할 때 우 리 는 이미 지 를 몇 개의 특정한,독특한 성질 을 가 진 구역 으로 나 누 어야 한다.각 구역 은 하나의 픽 셀 의 집합 을 대표 하고 모든 집합 은 하나의 물 체 를 대표 한다.이 과정 을 완성 하 는 기술 은 보통 이미지 분할 이 라 고 하 는데 이것 은 이미지 처리 에서 이미지 분석 까지 의 관건 적 인 절차 이다.사실 이 과정 은 이해 하기 어렵 지 않다.마치 우리 인류 가 경 치 를 보 는 것 과 같다.우리 가 본 세 계 는 수많은 물체 로 구성 되 어 있다.마치 교실 은 사람,책상,책,칠판 등 으로 구성 되 어 있다.우 리 는 한도 값 처 리 를 통 해 배경 에서 우리 의 연구 대상 을 분리 할 수 있 기 를 바 라 는 것 이다.
테두리 검출
Otsu 한도 값 분할 을 거 친 후에 우 리 는 이미지 에 대해 가장자리 검 사 를 해 야 한다.우 리 는 여기 서 Canny 가장자리 검 사 를 사용 하고 처리 한 결 과 는 다음 과 같다.

그 다음 에 폐 연산 과 연산 을 한 번 더 진행 하여 흰색 물체 안의 작은 검은색 구멍 구역 을 채 우 고 경 계 를 부 드 럽 게 합 니 다.처리 후의 효 과 는 다음 과 같 습 니 다.

이때,자동차 번호판 의 윤곽 은 이미 초보 적 으로 뽑 혔 지만,단지 약간의 흰색 덩어리 가 방해 하고 있 을 뿐이다.
상기 과정의 코드:

//     
bool contour(Mat image, vector<vector<Point>> &contours, vector<Vec4i> &hierarchy) {
 Mat img_gau, img_open, img_seg, img_edge;
 //     
 GaussianBlur(image, img_gau, Size(7, 7), 0, 0);
 //    
 Mat element = getStructuringElement(MORPH_RECT, Size(23, 23));
 morphologyEx(img_gau, img_open, MORPH_OPEN, element);
 addWeighted(img_gau, 1, img_open, -1, 0, img_open);
 //     
 threshold(img_open, img_seg, 0, 255, THRESH_BINARY + THRESH_OTSU);
 //     
 Canny(img_seg, img_edge, 200, 100);
 element = getStructuringElement(MORPH_RECT, Size(22, 22));
 morphologyEx(img_edge, img_edge, MORPH_CLOSE, element);
 morphologyEx(img_edge, img_edge, MORPH_OPEN, element);
 findContours(img_edge, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
 return true;
}
윤곽 선택
지금 우 리 는 이미 윤곽 이 생 겼 다.우 리 는 자동차 번호판 이 있 는 그 윤곽 을 선별 해 야 한다.자동차 번호판 의 너비 와 높 은 비례 가 고정 되 어 있 기 때문에 이 기하학 적 특징 에 따라 우 리 는 선별 을 해 야 한다.효 과 는 그림 과 같다.


코드 는 다음 과 같 습 니 다:

//      
Point2f(*choose_contour(vector<vector<Point>> contours))[2] {
 int size = (int)contours.size();
 int i_init = 0;
 Point2f (*contours_result)[2] = new Point2f[size][2];
 for (int i = 0; i < size; i++){
 //       
 RotatedRect number_rect = minAreaRect(contours[i]);
 Point2f rect_point[4];
 number_rect.points(rect_point);
 float width = rect_point[0].x - rect_point[1].x;
 float height = rect_point[0].y - rect_point[3].y;
 //       
 if (width < height) {
 float temp = width;
 width= height;
 height = temp;
 }
 float ratio = width / height;
 if (2.5 < ratio && ratio < 5.5) {
 contours_result[i_init][0] = rect_point[0];
 contours_result[i_init][1] = rect_point[2];
 i_init++;
 }
 
 }
 return contours_result;
}

//       
int license_gain(Point2f (*choose_license)[2], Mat img) {
 int size = (int)(_msize(choose_license) / sizeof(choose_license[0]));
 //     
 for (int i = 0; i < size; i++) {
 if ((int)choose_license[i][0].x > 1 && (int)choose_license[i][0].y > 1) {
 int x = (int)choose_license[i][1].x;
 int y = (int)choose_license[i][1].y;
 int width = (int)(choose_license[i][0].x) - (int)(choose_license[i][1].x);
 int height = (int)(choose_license[i][0].y) - (int)(choose_license[i][1].y);
 Rect choose_rect(x, y, width, height);
 Mat number_img = img(choose_rect);
 rectangle(img, choose_license[i][0], choose_license[i][1], Scalar(0, 0, 255), 2, 1, 0);
 imshow("      " + to_string(i), number_img);
 }
 }
 imshow("    ", img);
 return 0;
}
마지막 main 함수:

int main() {
 //     
 Mat img = imread("license.jpg");
 Mat gray_img;
 //       
 cvtColor(img, gray_img, CV_BGR2GRAY);
 //     
 vector<vector<Point>> contours;
 vector<Vec4i> hierarchy;
 contour(gray_img, contours, hierarchy);
 //     
 Point2f (*choose_license)[2] = choose_contour(contours);
 license_gain(choose_license, img);
 delete [] choose_license;
 waitKey(0);
 return 0;
}
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기