자동차 번호판 분석, 이미지추출/저장, 텍스트 추출 프로그램

C++ 차량 번호판 분석 프로그램

한글 인식은 개선 필요.

// Main.cpp


#include "Main.h"
#include "Main_function.h"



///////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv) {

	string plateNum = "";
	string result_plateNum, result_image;
	Mat image, image0, image_wrap, image1, image2, image3, image4, image5, image6, image7, image8, image9, image10;           // input image
	Mat imgGrayscaleScene, imgThreshScene;
	RNG rng;

	//plateNum = "09.jpg";
	//SHOW_STEPS = 1;
	//SHOW_RESULT = 1;



	///*
	if (argv[1]) {
		if (argv[1] == string("help")) {
			cout << "exe [image file path/name] [option]" << endl;
			cout << "[option] log : 진행 과정의 이미지를 보여줌" << endl;
			cout << "[option] result : 결과이미지를 현재 경로에 저장" << endl;
			cout << "[option] reslog : 진행 과정의 이미지를 보여줌 & 결과이미지를 현재 경로에 저장" << endl;
			return(0);
		}
		else {
			plateNum = argv[1];
		}
	}
	else {
		cout << "파라미터(이미지파일)가 없습니다." << endl;
		cout << "ex) car_plate_detection.exe help" << endl;
		return(0);
	}

	// 출력결과 파일명
	if (argv[2]) {
		if (argv[2] == string("log")) {
			SHOW_STEPS = 1;
		}
		else if (argv[2] == string("result") || argv[2] == string("res")) {
			SHOW_RESULT = 1;
		}
		else if (argv[2] == string("reslog") || argv[2] == string("logres")) {
			SHOW_STEPS = 1;
			SHOW_RESULT = 1;
		}
		else {
			result_image = argv[2];
		}

	}
	//*/



	if (SHOW_STEPS == 1) {
		destroyAllWindows();
		
		//	cv::namedWindow("win");
		//	cv::moveWindow("win", 0, 20);
		cv::namedWindow("win0");
		cv::moveWindow("win0", 400, 20);
		cv::namedWindow("win1");
		cv::moveWindow("win1", 800, 20);
		cv::namedWindow("win2");
		cv::moveWindow("win2", 1200, 20);
		cv::namedWindow("win3");
		cv::moveWindow("win3", 0, 300);
		cv::namedWindow("win4");
		cv::moveWindow("win4", 400, 300);
		cv::namedWindow("win5");
		cv::moveWindow("win5", 800, 300);
		cv::namedWindow("win6");
		cv::moveWindow("win6", 1200, 300);
		cv::namedWindow("win7");
		cv::moveWindow("win7", 0, 800);
		cv::namedWindow("win8");
		cv::moveWindow("win8", 400, 800);
		cv::namedWindow("win9");
		cv::moveWindow("win9", 800, 800);
		cv::namedWindow("win10");
		cv::moveWindow("win10", 1200, 800);
		cv::namedWindow("win11");
		cv::moveWindow("win11", 1200, 900);
	}

	image = cv::imread(plateNum);         // open image
	if (image.empty()) {
		cout << plateNum << "는 이미지 파일이 아닙니다." << endl;
		return(0);
	}

	bool blnKNNTrainingSuccessful = loadKNNDataAndTrainKNN();





	//resize(image, image0, cv::Size(1200, 935), 0, 0, cv::INTER_AREA);
	//resize(image, image0, cv::Size(), 1.5, 1.5);
	//Rect rect(80, 120, 640, 300);
	//image0 = image(rect);
	int img_width = image.size().width;
	int img_height = image.size().height;
	int img_width2, img_height2;
	

	Point2f inputQuad[4];
	Point2f outputQuad[4];

	Mat lambda(2, 4, CV_32FC1);
	Mat input, output;


	for (int loop = 0; loop <= 12; loop++) {
	
		if (loop == 0) {
			//image0 = image.clone();
			img_height2 = 300;
			img_width2 = getWidth(img_width, img_height, img_height2);
			resize(image, image0, cv::Size(img_width2, img_height2), 0, 0, cv::INTER_AREA);
		}
		else if (loop == 1) {
			img_height2 = 400;
			img_width2 = getWidth(img_width, img_height, img_height2);
			resize(image, image0, cv::Size(img_width2, img_height2), 0, 0, cv::INTER_AREA);
		}
		else if (loop == 2) {
			// 사진 하단 양쪽 늘임

			input = image0.clone();
			lambda = Mat::zeros(input.rows, input.cols, input.type());

			inputQuad[0] = Point2f(0, 0);
			inputQuad[1] = Point2f(input.cols, 0);
			inputQuad[2] = Point2f(input.cols, input.rows);
			inputQuad[3] = Point2f(0, input.rows);

			outputQuad[0] = Point2f(0, 0);
			outputQuad[1] = Point2f(input.cols, 0);
			outputQuad[2] = Point2f(input.cols + 100, input.rows);
			outputQuad[3] = Point2f(-100, input.rows);

			lambda = getPerspectiveTransform(inputQuad, outputQuad);
			warpPerspective(input, output, lambda, output.size());

			image0 = output.clone();
			image_wrap = image0.clone();
		}
		else if (loop == 3) {
			// 사진 좌측 위아래 늘임

			input = image_wrap.clone();

			outputQuad[0] = Point2f(-120, -80);
			outputQuad[1] = Point2f(input.cols, 0);
			outputQuad[2] = Point2f(input.cols, input.rows);
			outputQuad[3] = Point2f(0, input.rows + 150);

			lambda = getPerspectiveTransform(inputQuad, outputQuad);
			warpPerspective(input, output, lambda, output.size());

			image0 = output.clone();


		}
		else if (loop == 4) {
			// 사진 우측 위아래 늘임
			input = image_wrap.clone();

			outputQuad[0] = Point2f(0, 0);
			outputQuad[1] = Point2f(input.cols + 120, -80);
			outputQuad[2] = Point2f(input.cols, input.rows + 150);
			outputQuad[3] = Point2f(0, input.rows);

			lambda = getPerspectiveTransform(inputQuad, outputQuad);
			warpPerspective(input, output, lambda, output.size());

			image0 = output.clone();


		}
		else if (loop == 5) {
			// 사진 좌측 하단 늘임
			input = image_wrap.clone();

			outputQuad[0] = Point2f(0, 100);
			outputQuad[1] = Point2f(input.cols, 0);
			outputQuad[2] = Point2f(input.cols, input.rows);
			outputQuad[3] = Point2f(0, input.rows + 350);

			lambda = getPerspectiveTransform(inputQuad, outputQuad);
			warpPerspective(input, output, lambda, output.size());

			image0 = output.clone();

		}
		else if (loop == 6) {
			// 사진 우측 하단 늘임
			input = image_wrap.clone();

			outputQuad[0] = Point2f(0, 0);
			outputQuad[1] = Point2f(input.cols, 100);
			outputQuad[2] = Point2f(input.cols, input.rows + 350);
			outputQuad[3] = Point2f(0, input.rows);

			lambda = getPerspectiveTransform(inputQuad, outputQuad);
			warpPerspective(input, output, lambda, output.size());

			image0 = output.clone();

		}
		else if (loop == 7) {
			img_height2 = 600;
			img_width2 = getWidth(img_width, img_height, img_height2);
			resize(image, image0, cv::Size(img_width2, img_height2), 0, 0, cv::INTER_AREA);

			int pos_x = image0.size().width / 2 - 300;
			int pos_y = img_height2 / 2 - 100;
			Rect rect(pos_x, pos_y, 600, 400);
			image0 = image0(rect);
		}
		else if (loop == 8) {
			img_height2 = 800;
			img_width2 = getWidth(img_width, img_height, img_height2);
			resize(image, image0, cv::Size(img_width2, img_height2), 0, 0, cv::INTER_AREA);

			int pos_x = image0.size().width / 2 - 300;
			int pos_y = img_height2 / 2 - 100;
			Rect rect(pos_x, pos_y, 600, 400);
			image0 = image0(rect);
		}
		else if (loop == 9) {
			img_height2 = 1000;
			img_width2 = getWidth(img_width, img_height, img_height2);
			resize(image, image, cv::Size(img_width2, img_height2), 0, 0, cv::INTER_AREA);

			Rect rect(0, 700, img_width2, 300);
			image0 = image(rect);
		}
		else if (loop == 10) {

			Rect rect(0, 500, img_width2, 300);
			image0 = image(rect);
		}
		else if (loop == 11) {

			Rect rect(0, 300, img_width2, 300);
			image0 = image(rect);
		}
		else if (loop == 12) {

			Rect rect(0, 100, img_width2, 300);
			image0 = image(rect);
		}

		
		if (SHOW_STEPS == 1) {
			cout << "try: " << loop << endl;
		}

		if (SHOW_STEPS == 1) {
			//	cv::imshow("win", image);			// 원본 이미지
			cv::imshow("win0", image0);			// 리사이즈 이미지
		}

		cvtColor(image0, image1, CV_BGR2GRAY);  //  gray-color 변환

		if (SHOW_STEPS == 1) {
			cv::imshow("win1", image1);
		}

		preprocess(image0, image2, image3);

		if (SHOW_STEPS == 1) {
			cv::imshow("win2", image2);
			cv::imshow("win3", image3);			// adaptive threshold(문턱값) / 영상 이진화	
			cv::waitKey(0);
		}


		image4 = cv::Mat(image3.size(), CV_8UC3, SCALAR_BLACK);
		int intCountOfPossibleChars = 0;

		vector<vector<cv::Point> > contours;
		vector<PossiblePlate> vectorOfPossiblePlates;
		vector<PossibleChar> vectorOfPossibleChars;


		findContours(image3, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);        // find all contours
		//findContours(image3, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
		//findContours(image3, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);


		for (unsigned int i = 0; i < contours.size(); i++) {                // for each contour
			drawContours(image4, contours, i, SCALAR_WHITE);


			PossibleChar possibleChar(contours[i]);

			if (checkIfPossibleChar(possibleChar)) {                // if contour is a possible char, note this does not compare to other chars (yet) . . .
				intCountOfPossibleChars++;                          // increment count of possible chars
				vectorOfPossibleChars.push_back(possibleChar);      // and add to vector of possible chars
			}
		}

		if (SHOW_STEPS == 1) {
			cv::imshow("win4", image4);			// contours(윤곽선1)
		}


		//vector<PossibleChar> vectorOfPossibleCharsInScene = findPossibleCharsInScene(image3);
		vector<vector<PossibleChar> > vectorOfVectorsOfMatchingCharsInScene = findVectorOfVectorsOfMatchingChars(vectorOfPossibleChars);

		if (SHOW_STEPS == 1) {
			image5 = cv::Mat(image4.size(), CV_8UC3, SCALAR_BLACK);

			for (auto &vectorOfMatchingChars : vectorOfVectorsOfMatchingCharsInScene) {
				int intRandomBlue = rng.uniform(0, 256);
				int intRandomGreen = rng.uniform(0, 256);
				int intRandomRed = rng.uniform(0, 256);

				vector<vector<cv::Point> > contours;

				for (auto &matchingChar : vectorOfMatchingChars) {
					contours.push_back(matchingChar.contour);
					//cout << "char: " << matchingChar.contour << endl;
				}
				cv::drawContours(image5, contours, -1, cv::Scalar((double)intRandomBlue, (double)intRandomGreen, (double)intRandomRed));
			}

			cv::imshow("win5", image5);
			cv::waitKey(0);
		}


		for (auto &vectorOfMatchingChars : vectorOfVectorsOfMatchingCharsInScene) {
			PossiblePlate possiblePlate = extractPlate(image0, vectorOfMatchingChars);

			if (possiblePlate.imgPlate.empty() == false) {
				vectorOfPossiblePlates.push_back(possiblePlate);

			}
		}

		if (SHOW_STEPS == 1) {
			for (unsigned int i = 0; i < vectorOfPossiblePlates.size(); i++) {
				cv::Point2f p2fRectPoints[4];

				vectorOfPossiblePlates[i].rrLocationOfPlateInScene.points(p2fRectPoints);

				for (int j = 0; j < 4; j++) {
					cv::line(image5, p2fRectPoints[j], p2fRectPoints[(j + 1) % 4], SCALAR_RED, 2);
				}
				cv::imshow("win6", image5);
				cv::imshow("win7", vectorOfPossiblePlates[i].imgPlate);

				cv::waitKey(0);
			}
		}




		// 추출된 번호판 숫자 인식

		for (auto &possiblePlate : vectorOfPossiblePlates) {

			int img_height2 = 100;
			int img_width2 = getWidth(possiblePlate.imgPlate.cols, possiblePlate.imgPlate.rows, img_height2);

			preprocess(possiblePlate.imgPlate, possiblePlate.imgGrayscale, possiblePlate.imgThresh);
			resize(possiblePlate.imgThresh, possiblePlate.imgThresh, cv::Size(img_width2, img_height2), 0, 0, cv::INTER_AREA);

			// threshold again to eliminate any gray areas
			cv::threshold(possiblePlate.imgThresh, possiblePlate.imgThresh, 0.0, 255.0, CV_THRESH_BINARY | CV_THRESH_OTSU);


			if (SHOW_STEPS == 1) {

				cv::imshow("win8", possiblePlate.imgThresh);	//  5d
			}


			vector<PossibleChar> vectorOfPossibleCharsInPlate = findPossibleCharsInPlate(possiblePlate.imgGrayscale, possiblePlate.imgThresh);

			if (SHOW_STEPS == 1) {
				image6 = cv::Mat(possiblePlate.imgThresh.size(), CV_8UC3, SCALAR_BLACK);
				contours.clear();

				for (auto &possibleChar : vectorOfPossibleCharsInPlate) {
					contours.push_back(possibleChar.contour);
				}

				cv::drawContours(image6, contours, -1, SCALAR_WHITE);

				cv::imshow("win9", image6);	// 6
			}

			vector<vector<PossibleChar> > vectorOfVectorsOfMatchingCharsInPlate = findVectorOfVectorsOfMatchingChars(vectorOfPossibleCharsInPlate);

			if (SHOW_STEPS == 1) {
				image7 = cv::Mat(possiblePlate.imgThresh.size(), CV_8UC3, SCALAR_BLACK);

				contours.clear();

				for (auto &vectorOfMatchingChars : vectorOfVectorsOfMatchingCharsInPlate) {
					int intRandomBlue = rng.uniform(0, 256);
					int intRandomGreen = rng.uniform(0, 256);
					int intRandomRed = rng.uniform(0, 256);

					for (auto &matchingChar : vectorOfMatchingChars) {
						contours.push_back(matchingChar.contour);
					}
					cv::drawContours(image7, contours, -1, cv::Scalar((double)intRandomBlue, (double)intRandomGreen, (double)intRandomRed));
				}
				cv::imshow("win10", image7);	// 7
			}

			if (vectorOfVectorsOfMatchingCharsInPlate.size() == 0) {                // if no groups of matching chars were found in the plate
				possiblePlate.strChars = "";            // set plate string member variable to empty string
														//possiblePlate.strChars.clear();
				continue;                               // go back to top of for loop
			}

			for (auto &vectorOfMatchingChars : vectorOfVectorsOfMatchingCharsInPlate) {                                         // for each vector of matching chars in the current plate
				sort(vectorOfMatchingChars.begin(), vectorOfMatchingChars.end(), PossibleChar::sortCharsLeftToRight);      // sort the chars left to right
				vectorOfMatchingChars = removeInnerOverlappingChars(vectorOfMatchingChars);                                     // and eliminate any overlapping chars
			}


			unsigned int intLenOfLongestVectorOfChars = 0;
			unsigned int intIndexOfLongestVectorOfChars = 0;


			for (unsigned int i = 0; i < vectorOfVectorsOfMatchingCharsInPlate.size(); i++) {
				if (vectorOfVectorsOfMatchingCharsInPlate[i].size() > intLenOfLongestVectorOfChars) {
					intLenOfLongestVectorOfChars = vectorOfVectorsOfMatchingCharsInPlate[i].size();
					intIndexOfLongestVectorOfChars = i;
				}
			}
			// suppose that the longest vector of matching chars within the plate is the actual vector of chars
			vector<PossibleChar> longestVectorOfMatchingCharsInPlate = vectorOfVectorsOfMatchingCharsInPlate[intIndexOfLongestVectorOfChars];


			if (SHOW_STEPS == 1) {
				image8 = cv::Mat(possiblePlate.imgThresh.size(), CV_8UC3, SCALAR_BLACK);

				contours.clear();

				for (auto &matchingChar : longestVectorOfMatchingCharsInPlate) {
					contours.push_back(matchingChar.contour);
				}
				cv::drawContours(image8, contours, -1, SCALAR_WHITE);

				cv::imshow("win11", image8);
			}

			possiblePlate.strChars = recognizeCharsInPlate(possiblePlate.imgThresh, longestVectorOfMatchingCharsInPlate, result_image);


			if (possiblePlate.strChars != "") {
				replace(possiblePlate.strChars, " ", "");
				int len = possiblePlate.strChars.size();
				if (SHOW_STEPS == 1) {
					cout << "[ " << possiblePlate.strChars << " ]" << endl;
				}
				if (len == 6) {
					result_plateNum = possiblePlate.strChars;
					break;
				}
			}


		}


		if (result_plateNum != "") {
			cout << result_plateNum << endl;
			break;
		}

		

		cv::waitKey(0);

	
	}




	return(0);


}