OpenCV 자동차 번호판 문자 분할 실현(C++)
먼저 원 도 를 살 펴 보 자.
가장 왼쪽 에 있 는 한 자 는 원래 상하 이 인 데 캡 처 할 때 오른쪽 에 있 는 작은 부분 만 얻 었 다.이것 은 원래 의 그림 과 얻 는 방법 과 관련 이 있다.천,상하 이...이런 좌우 로 분 리 된 글 자 는 이런 문제 가 자주 발생 하고 방법 을 최적화 한 후에 해결 할 수 있 으 며 여 기 는 잠시 토론 하지 않 는 다.
뒤의 글 자 는 모두 완전 하고 문자 분할 과정 은 영향 을 받 지 않 습 니 다.먼저 일반적인 작업 을 하고 처리 하기 편리 하도록 그 레이스 케 일 그림 으로 바 꿉 니 다.
분할 하 는 방법 은 한 가지 가 아니다.가장 간단 한 것 은 인공 성분 을 많이 넣 고 대체적인 너비 에 따라 다시 미세 조정 하여 절단 하 는 것 이다.그러나 이렇게 하면 가장 빠 른 것 처럼 보이 지만 사실은 원가 가 가장 높 고 단일 한 그림 에 만 적합 하기 때문에 이런 잘못 사용 이 낮 고 자동 적 이지 못 한 방법 은 고려 하지 않 는 다.
현재 저 는 두 가지 다른 방법 을 사 용 했 습 니 다.하 나 는 가장자리 검 사 를 한 다음 에 윤곽 을 검 측 하고 문자 의 윤곽 특징 에 따라 문 자 를 선별 하 는 것 입 니 다.다른 하 나 는 픽 셀 값 판단 입 니 다.주로 픽 셀 수량 에 따라 수평 맵 을 사용 하여 폭 을 절단 합 니 다.수직 맵 은 높이 가 기본적으로 일치 하기 때문에 필요 하지 않 습 니 다.방법 은 수평 맵 과 같 습 니 다.
두 가지 방법 을 나 는 모두 뒤에 써 서 필요 에 따라 스스로 복제 했다.픽 셀 값 으로 판단 하려 면 그 레이스 케 일 을 이치 화 그림 으로 바 꾸 고 한도 값 으로 분할 하면 된다.첫 번 째 윤곽 으로 나 누 는 방법 을 사용 하면 그 레이스 케 일과 이치 화 그림 이 모두 가능 하 며 결 과 는 별 차이 가 없다.
윤곽 을 검사 하여 분할 하 다.
테두리 검출
그림 에 대해 테두리 검 사 를 실시 합 니 다.여 기 는 Canny 테두리 검 사 를 사 용 했 습 니 다.처 리 된 결 과 는 다음 과 같 습 니 다.
모든 글자 의 가장자리 가 그 려 진 것 을 볼 수 있 고 그 다음 에 모든 글자 의 윤곽 을 얻 을 수 있다.
검 측 윤곽
find Contours()를 사용 하여 모든 윤곽 을 추출 한 다음 원 그림 에 효 과 를 보 여 줍 니 다.
모든 글자 가 틀 에 박 힌 것 이 아니 라 내부 와 이미지 에 나타 난 특수 한 부분의 윤곽 도 있 음 을 알 수 있 습 니 다.그 다음 에 우 리 는 모든 글자 의 대략적인 크기 에 따라 우리 가 원 하 는 결 과 를 선별 할 것 입 니 다.
이렇게 보면 성공 한 것 같 지 않 습 니까?그리고 윤곽 위치 에 따라 모든 글 자 를 추출 하면 됩 니 다.그러나 여기 서 모든 윤곽 의 앞 뒤 순 서 는 반드시 이미지 의 위치 가 아 닙 니 다.여기 서 저 는 모든 윤곽 왼쪽 상단 횡 좌표 x 의 크기 로 정렬 합 니 다.
전체 코드:
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <map>
using namespace std;
using namespace cv;
int main() {
Mat img = imread("number.jpg");
Mat gray_img;
//
cvtColor(img, gray_img, CV_BGR2GRAY);
//
Mat img_gau;
GaussianBlur(gray_img, img_gau, Size(3, 3), 0, 0);
//
Mat img_seg;
threshold(img_gau, img_seg, 0, 255, THRESH_BINARY + THRESH_OTSU);
// ,
Mat img_canny;
Canny(img_seg, img_canny, 200, 100);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(img_canny, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point());
int size = (int)(contours.size());
//
vector<int> num_order;
map<int, int> num_map;
for (int i = 0; i < size; i++) {
//
Rect number_rect = boundingRect(contours[i]);
int width = number_rect.width;
int height = number_rect.height;
// ,
if (width > img.cols/10 && height > img.rows/2) {
rectangle(img_seg, number_rect.tl(), number_rect.br(), Scalar(255, 255, 255), 1, 1, 0);
num_order.push_back(number_rect.x);
num_map[number_rect.x] = i;
}
}
//
sort(num_order.begin(), num_order.end());
for (int i = 0; i < num_order.size(); i++) {
Rect number_rect = boundingRect(contours[num_map.find(num_order[i])->second]);
Rect choose_rect(number_rect.x, 0, number_rect.width, gray_img.rows);
Mat number_img = gray_img(choose_rect);
imshow("number" + to_string(i), number_img);
// imwrite("number" + to_string(i) + ".jpg", number_img);
}
imshow(" ", gray_img);
waitKey(0);
return 0;
}
픽 셀 값 판단 분할분할 방법:먼저 각 열의 픽 셀 값 이 0 보다 큰 픽 셀 개수 가 5 개 를 초과 하 는 지 판단 할 때 이 열 은 숫자 가 있다 고 생각 하고 각 열의 픽 셀 이 5 보다 큰 지 기록 하여 하나의 배열 을 만 듭 니 다.
// 1
int pixrow[1000];
for (int i = 0; i < roi_col - 1; i++) {
for (int j = 0; j < roi_row - 1; j++) {
pix = img_threadhold.at<uchar>(j, i);
pixrow[i] = 0;
if (pix > 0) {
pixrow[i] = 1;
break;
}
}
}
// ,
for (int i = 2; i < roi_col - 1 - 2; i++) {
if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) >= 3) {
pixrow[i] = 1;
}
else if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) <= 1) {
pixrow[i] = 0;
}
}
그 다음 에 픽 셀 이 0 과 1 의 연속 적 인 길 이 를 기록 하여 문자 의 너 비 를 계산 하고 마지막 으로 너비 의 크기 로 문 자 를 선별 합 니 다.
//
int count = 0;
bool flage = false;
for (int i = 0; i < roi_col - 1; i++) {
pix = pixrow[i];
if (pix == 1 && !flage) {
flage = true;
position1[count] = i;
continue;
}
if (pix == 0 && flage) {
flage = false;
position2[count] = i;
count++;
}
if (i == (roi_col - 2) && flage) {
flage = false;
position2[count] = i;
count++;
}
}
분 할 된 결과:전체 코드:
#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("number.jpg");
Mat gray_img;
//
cvtColor(img, gray_img, CV_BGR2GRAY);
//
Mat img_gau;
GaussianBlur(gray_img, img_gau, Size(3, 3), 0, 0);
//
Mat img_threadhold;
threshold(img_gau, img_threadhold, 0, 255, THRESH_BINARY + THRESH_OTSU);
//
int roi_col = img_threadhold.cols, roi_row = img_threadhold.rows, position1[50], position2[50], roi_width[50];
uchar pix;
// 1
int pixrow[1000];
for (int i = 0; i < roi_col - 1; i++) {
for (int j = 0; j < roi_row - 1; j++) {
pix = img_threadhold.at<uchar>(j, i);
pixrow[i] = 0;
if (pix > 0) {
pixrow[i] = 1;
break;
}
}
}
// ,
for (int i = 2; i < roi_col - 1 - 2; i++) {
if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) >= 3) {
pixrow[i] = 1;
}
else if ((pixrow[i - 1] + pixrow[i - 2] + pixrow[i + 1] + pixrow[i + 2]) <= 1) {
pixrow[i] = 0;
}
}
//
int count = 0;
bool flage = false;
for (int i = 0; i < roi_col - 1; i++) {
pix = pixrow[i];
if (pix == 1 && !flage) {
flage = true;
position1[count] = i;
continue;
}
if (pix == 0 && flage) {
flage = false;
position2[count] = i;
count++;
}
if (i == (roi_col - 2) && flage) {
flage = false;
position2[count] = i;
count++;
}
}
//
for (int n = 0; n < count; n++) {
roi_width[n] = position2[n] - position1[n];
}
// 、 ,
int max = roi_width[0], max_index = 0;
int min = roi_width[0], min_index = 0;
for (int n = 1; n < count; n++) {
if (max < roi_width[n]) {
max = roi_width[n];
max_index = n;
}
if (min > roi_width[n]) {
min = roi_width[n];
min_index = n;
}
}
int index = 0;
int new_roi_width[50];
for (int i = 0; i < count; i++) {
if (i == min_index || i == max_index) {}
else {
new_roi_width[index] = roi_width[i];
index++;
}
}
//
int avgre = (int)((new_roi_width[count - 3] + new_roi_width[count - 4] + new_roi_width[count - 5]) / 3.0);
// ,
int licenseX[10], licenseW[10], licenseNum = 0;
int countX = 0;
for (int i = 0; i < count; i++) {
if (roi_width[i] >(avgre - 8) && roi_width[i] < (avgre + 8)) {
licenseX[licenseNum] = position1[i];
licenseW[licenseNum] = roi_width[i];
licenseNum++;
countX++;
continue;
}
if (roi_width[i] > (avgre * 2 - 10) && roi_width[i] < (avgre * 2 + 10)) {
licenseX[licenseNum] = position1[i];
licenseW[licenseNum] = roi_width[i];
licenseNum++;
}
}
//
Mat number_img = Mat(Scalar(0));
for (int i = 0; i < countX; i++) {
Rect choose_rect(licenseX[i], 0, licenseW[i], gray_img.rows);
number_img = gray_img(choose_rect);
imshow("number" + to_string(i), number_img);
// imwrite("number" + to_string(i) + ".jpg", number_img);
}
waitKey(0);
return 0;
}
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Visual Studio 2017에서 OpenCV 템플릿 프로젝트 만들기・Windows 7 Professional 64bit ・Visual Studio 2017 Version 15.9.14 · OpenCV 3.4.1 OpenCV의 도입 방법 등은 아래를 참조하십시오. Visual Stu...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.