LPR车牌识别系统搭建

       在写这篇文章之前,我需要好好感谢一下,今天帮了我一个下午的同事。小姐姐经验丰富,完全惊到我了。一个错误去请教,2分钟解决。

       今天主要是把lpr系统导入vs2017中,配置环境,并且调用写好的识别算法。我的项目主要分为三个部分:摄像头视频拍摄并且截取有效车牌;lpr系统处理车牌并通过串口发送到单片机;单片机接受串口信号并作出相应动作。

        在开始之前,分享一下需要用到的资源:

       lpr源代码:github是个好东西。虽然我也不太会用,英文不好)

            https://github.com/zeusees/HyperLPR.git

       lpr简介(1-8):

            http://www.cnblogs.com/silence-hust/p/4191655.html

       opencv3.3下载:(还有其他版本,自己去官网下吧)

            https://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.3.0/opencv-3.3.0-vc14.exe/download

       第一部分是摄像头视频拍摄:摄像头视频拍摄是调用VideoCapture函数打开摄像头,使用read(cv::Mat)函数读出图像。在这里呢,我们预想是用激光测距来响应车辆来往,但是东西还没到,我就手动用控制台写了一个信号触发。加了一个线程,阻塞在输入函数这里。当输入0时触发响应,主线程的函数截取当前视频保存在本地目录下。在这里我犯了第一个错误:

    错误一:

     错误描述:我是想共用一个cv::Mat对象,主线程不停地通过cap.read(image)(VideoCapture声明出来的对象)不停地输出视频。然后当线程2输入触发信号后,对image对象加锁,在线程2中读取线程。

     错误说明:在多线程时,一个读,一个写是安全的,不需要加锁。并且用flag作为触发标志,当触发时,直接在主线程中截取就可以避免一个读一个写的乱序问题。顺便吐槽一下,windows底下的函数是真的难用。附上解决代码:(这段代码只需要配一下opencv环境就可以使用了。至于vs下opencv环境如何配置明天也可以写一下,我自己也经常忘,哈哈,说不定这个教程是写给我自己以后看的。)

     错误二:

     错误描述:cv::VideoCapture cap(1)这个函数,参数:-1为自己选择(会有一个错误四的小坑),0为自带摄像头,1-n是usb摄像头选择。

     错误说明:我每次都不知道这个参数是几,从1一路试过来。哈哈,你也自己这么玩去吧。

#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <Windows.h>
DWORD WINAPI myfun(LPVOID lpParameter);
bool flag = false;

int main(int argc, char** argv)
{
	HANDLE hThread;
	cv::Mat image;
	int option = 0;
	hThread = CreateThread(NULL, 0, myfun, NULL, 0, NULL);	//开始摄像
	while (true) {
		std::cout << "请选择是否截图(1:是,else:否)" << std::endl;
		std::cin >> option;
		switch (option) {
		case 1:
			flag = true;
			std::cout << "截图成功!" << std::endl;
			break;
		default:break;
		}
	}
	return 0;
}

DWORD WINAPI myfun(LPVOID lpParameter) {
	static int num = 0;
	cv::VideoCapture cap(1);
	cv::Mat frame;
	char imageAddress[30] = "F:\\image1.jpg";

	if (cap.isOpened())
		std::cout << "摄像头打开成功!" << std::endl;
	while (true) {
		cap.read(frame);
		if (flag) {
			sprintf_s(imageAddress, "%s%d%s", "F:\\image", num, ".jpg");
			cv::imwrite(imageAddress, frame);
			flag = false;
		}
		if (!frame.empty())
			cv::imshow("frame", frame);
		else
			std::cout << "error" << std::endl;
		cv::waitKey(100);
	}
}

       

 

    第二部分 是lpr系统识别已保存的车牌并将其通过串口传给单片机:

        我们本来的想法是通过网口传给板子(tcp连接),但是公司的嵌入式大神刚刚离职,进了我这样一个小渣渣。只能先用32将就将就了。并且32还没有网口模块,只能先用串口将就将就了,汗。lpr系统呢,主要分为图像采集,图像处理,数据库管理三个部分(见图1)。图像采集是我负责的,设备不足,就先用usb摄像头将就了(画质模糊,还没有补光灯),数据库管理也先不管(其实是我也不清楚)。图像处理呢分为五个部分预处理,车牌定位,倾斜校正,字符分割,字符识别。具体见图2,详细的我也不多说了,参考silcenceer大神的博客吧:http://www.cnblogs.com/silence-hust/p/4191655.html。恩恩大神分成了六个模块,可只写了五个模块。可能是我视力不好吧,有兴趣的同学可以去瞅瞅,他的图2.2上面一行,哈哈。

       

 

       

         以上的全是废话,请见谅。下面进入正题:如何把lpr导入vs中。

        1.下好lpr的源文件,包括这些东西:include,model,src,tests。include中全是头文件,src中是.cpp文件(.h的实现),tests文件中是例程。好了有人问还有model呢,恩,我不说当然是···我不知道啦(等周一去问小姐姐去)。

        2.把lpr的文件夹放在:文件名/工程名/lpr这里,lpr要是放错应该也会有影响的。然后新建vs的空项目,把头文件全部导入进来,.cpp文件也导入进来(tests中的.cpp就别导入进来捣乱了,多几个main,你让程序咋跑)。见图3,配置目录

        3.设置环境变量,这步是关键。项目->属性->链接器->输入->附加依赖项:opencv_world330.lib。在这里我被坑的很惨,我添加了opencv_world330d.lib和opencv_world330.lib,这两个产生混乱,无法识别。具体为啥我也不知道,小姐姐一眼看穿···。对了你的环境也得是相应的,比如我的环境变量是opencv3.3(怎么配置看我后面的更新吧)。到这里。基本配置就完成了,后面是怎么使用了。

        错误三:

        错误描述:使用cv::imshow()函数,能正常运行,但是不能显示图片,呈现灰色图像(见图4)。

        错误说明 :共有两种可能导致:1.没有cv::waitKey(1),opencv需要等待一下;2.由于导入两个lib,导致opencv混乱,无法正确读入图片。其中第二种,坑了我一下午。

         4.调用lpr,这是别人的代码,我没法共享,不过函数中要导入下面这段代码··········。具体如何使用,见我分享的github项目中的“HyperLPR/Prj-Win/lpr/tests/test_pipeline.cpp”这个文件。参照着写吧。

    pr::PipelinePR prc("../lpr/model/cascade.xml",
                      "../lpr/model/HorizonalFinemapping.prototxt","../lpr/model/HorizonalFinemapping.caffemodel",
                      "../lpr/model/Segmentation.prototxt","../lpr/model/Segmentation.caffemodel",
                      "../lpr/model/CharacterRecognization.prototxt","../lpr/model/CharacterRecognization.caffemodel"
                    );

          5. 写串口,配置波特率等。std::string TEST_IMAGE(std::string imgname) 是她的识别函数,我调用了这个函数,识别返回的代码,然后打开串口,输出出去。这里有个小坑:

           错误四:

           错误描述:串口不能打开,并且第一部分的摄像头打开后显示不出来

           错误说明:串口和usb摄像头在电脑识别,都是串口,当你拔下来,并且没有按照原来次序插的时候,打开摄像头却打开了串口,所以显示不出来这样当你在用cv::VideoCapture cap(-1)时,可能会打开的是串口转TTL的那个串口,导致出现灰色画面。并且,第二部分的程序会因为串口被占用而闪退。这就是传说中的,我昨天还能好好跑的,怎么今天一试全是问题了。

            附上我第二部分主函数的代码(识别部分不是我写的,就不贴出来了。并且串口部分也荡的网的):

#include <iostream>
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <tchar.h>
#include <Windows.h>
#include <string>
#include <sstream>
#include <vector>
#include <cmath>
#include <stdlib.h>
#include "lpr\include\Pipeline.h"               //这个会根据你的lpr的地址改变,这个是一个大坑,我会在错误5中提到

HANDLE Serial_open(LPCWSTR, int);		//串口开启函数
int Serial_read(HANDLE, void*, int);		//串口读
int Serial_write(HANDLE, const void*, int);	//串口写
void Serial_close(HANDLE);			//串口关
void clear_buf(unsigned char*, int);		//清空字符串数组

std::string getStringFromFloat(float f)
{    /*****算法部分*****/
}
std::string TEST_IMAGE(std::string imgname) {

	pr::PipelinePR prc("F:/work/opencv/readSerial/readSerial/readSerial/lpr/model/cascade.xml",
		"F:/work/opencv/readSerial/readSerial/readSerial/lpr/model/HorizonalFinemapping.prototxt", 
		"F:/work/opencv/readSerial/readSerial/readSerial/lpr/model/HorizonalFinemapping.caffemodel",
		"F:/work/opencv/readSerial/readSerial/readSerial/lpr/model/Segmentation.prototxt",
		"F:/work/opencv/readSerial/readSerial/readSerial/lpr/model/Segmentation.caffemodel",
		"F:/work/opencv/readSerial/readSerial/readSerial/lpr/model/CharacterRecognization.prototxt",
		"F:/work/opencv/readSerial/readSerial/readSerial/lpr/model/CharacterRecognization.caffemodel"
	);

      /****算法部分***/
}

/**
open serial
@param COMx: eg:_T("COM1")
@param BaudRate:
return 0 success ,return Negative is haed err
*/
HANDLE Serial_open(LPCSTR COMx, int BaudRate) {
	HANDLE hCom;//串口设备句柄
	DCB dcb = { 0 };
	hCom = CreateFile(COMx,
		GENERIC_READ | GENERIC_WRITE,
		0,
		0,
		OPEN_EXISTING,
		0,//FILE_FLAG_OVERLAPPED,   //同步方式 或 重叠方式   
		0
	);

	if (hCom == INVALID_HANDLE_VALUE)
	{
		DWORD dwError = GetLastError();
		printf("Sorry, failed to open the serial\n");
		//return -1;  
		printf("The program will terminate in 3 seconds\n");
		Sleep(3000);
		exit(0);
	}
	else
		printf("The serial is successfully opened in a Baudrate %d!\n", BaudRate);

	dcb.DCBlength = sizeof(DCB);

	if (!GetCommState(hCom, &dcb))
	{
		DWORD dwError = GetLastError();
		return(HANDLE)(-1);
	}

	dcb.BaudRate = BaudRate;   //波特率   
	dcb.ByteSize = 8;          //位数   
	dcb.Parity = NOPARITY;     //奇偶检验   
	dcb.StopBits = ONESTOPBIT;  //停止位数   

	if (!SetCommState(hCom, &dcb))
	{
		DWORD dwError = GetLastError();
		return(HANDLE)(-1);
	}
	if (!PurgeComm(hCom, PURGE_RXCLEAR))   return(HANDLE)(-1);

	SetupComm(hCom, 1024, 1024);
	return hCom;
}

/**
serial read
@param Buf:data buf
@param size:
@return The len of read
*/
int Serial_read(HANDLE hCom, void* OutBuf, int size) {
	DWORD cnt = 0;
	ReadFile(hCom, OutBuf, size, &cnt, 0);
	return cnt;
}

/**
serial write
@param Buf:data buf
@param size:bytes of Buf
@return The len of writen
*/
int Serial_write(HANDLE hCom, const void* Buf, int size) {
	DWORD dw;
	WriteFile(hCom, Buf, size, &dw, NULL);
	return dw;
}

/**
serial close
*/
void Serial_close(HANDLE hCom) {
	CloseHandle(hCom);
}

/**
clear buf
*/
void clear_buf(unsigned char* buf, int N) {
	int i = 0;
	for (i = 0; i < N; i++) 
		buf[i] = 0;
	buf[i] = '\0';
}

/* main.cpp */
HANDLE hCom;
int main() {

	int len = 0;
	int i = 0;
	std::vector<char> vec;
	std::string strs;
	std::string strCur;
	std::string strPre;
	std::string pattern = ";";
	size_t begin = 0;
	size_t end = 0;
	char data[2];
	std::string strroad = "F:/image0.jpg";

	while (1) {
		cv::Mat img=cv::imread(strroad);
		if (img.empty()) {
			Sleep(1000);
			continue;
		}
		cv::imshow("image", img);
		cv::waitKey(30);

		strCur =TEST_IMAGE(strroad);
		if ((!strCur.empty()) &&  abs(strcmp(strCur.c_str(),strPre.c_str()))) {//&&(!strCur.compare(strPre)
			//切割字符串
			begin = end = 0;
			strs = strCur + pattern;
			end = strs.find(pattern);
			std::string lisPlate = strs.substr(begin, end);
			begin = end + 1;
			end = strs.find(pattern);
			std::string trust = strs.substr(begin, end);
			std::cout << lisPlate << std::endl;
			std::cout << trust << std::endl;
			//将string为float型
			float trustFloat = std::atof(trust.c_str());
			data[0] = trustFloat * 100;
			
			//发串口
			if (data[0] > 80) {
				hCom = Serial_open(_T("COM6"), 115200);
				Serial_write(hCom, data, 1);
				memset(data, 0, sizeof(data) / sizeof(char));
				Sleep(2000);
				Serial_close(hCom);
			}
		}
	}


	return 0;
}



    6.当你全部写好后,是不是很开心。那编译一下试试,编译一下你就知道,你丫的开心早了。我下午1点就到这一步了,一直到下午5点才全部搞定(还是在指导下进行的)。

    错误五:

    错误类型:满眼的error和warning。是不是跟我下面一样的···(这是我把路径改错给你们看到,感不感动。要是我改不回去,你们丫的也别开心,这篇文章你们也别想见到了,全给你删了。)

    错误说明:跟你说,这时候就要耐心,一个一个error点下去,点了十几个你看不懂的error后,你就会发现一个你能看得懂的error,找不到文件名。然后你就文件路径改掉,发现少掉一排warning和error。这时候,你可能机智的想到,会不会之前导进来的所有.cpp的库都是这样的错误,恩恩不错。当你全改掉后,开心啊,终于能编译通过了。你丫的能不能别开心这么早啊,前面就跟你说过了,别开心,坑还多着呢。

1>------ 已启动全部重新生成: 项目: readSerial, 配置: Debug x64 ------
1>main.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\cnnrecognizer.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\recognizer.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\fastdeskew.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\finemapping.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\main.cpp(195): warning C4244: “初始化”: 从“double”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\main.cpp(196): warning C4244: “=”: 从“float”转换到“char”,可能丢失数据
1>Recognizer.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\recognizer.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\recognizer.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>PlateSegmentation.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\platesegmentation.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\niblackthreshold.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>PlateDetection.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\platedetection.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\util.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\util.h(39): warning C4244: “参数”: 从“float”转换到“int”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\util.h(71): warning C4244: “return”: 从“double”转换到“float”,可能丢失数据
1>Pipeline.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\pipeline.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\pipeline.cpp : warning C4335: 检测到 Mac 文件格式: 请将源文件转换为 DOS 格式或 UNIX 格式
1>FineMapping.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\finemapping.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\finemapping.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\finemapping.cpp(182): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\finemapping.cpp(183): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\finemapping.cpp(184): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\finemapping.cpp(185): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>FastDeskew.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\include\fastdeskew.h : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(19): warning C4244: “return”: 从“double”转换到“int”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(38): warning C4267: “参数”: 从“size_t”转换到“int”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(70): warning C4244: “初始化”: 从“double”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(73): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(75): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(76): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(83): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(84): warning C4244: “参数”: 从“int”转换到“float”,可能丢失数据
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\fastdeskew.cpp(121): warning C4244: “初始化”: 从“__int64”转换到“int”,可能丢失数据
1>CNNRecognizer.cpp
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\cnnrecognizer.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
1>f:\work\opencv\readserial\readserial\readserial\lpr\src\cnnrecognizer.cpp(4): fatal error C1083: 无法打开包括文件: “include/CNNRecognizer.h”: No such file or directory
1>正在生成代码...
1>已完成生成项目“readSerial.vcxproj”的操作 - 失败。
========== 全部重新生成: 成功 0 个,失败 1 个,跳过 0 个 ==========

        错误六:你丫的,累似我了,你说我咋这么能遇到坑呢。

        错误描述:你们在完成前面工作后,会开心的发现,哇塞,warning和error的个数还是和没改之前差不多哎。附上我的报错,吓吓你。

        错误说明:由于函数模板max与Visual C++中的全局的宏max冲突。

        哎呀,没办法取消了,我也不会改回错误的形式了,就不展示了吧。 第二部分的错误差不多了。

 

        第三部分单片机接受串口信号并作出相应动作:

        下面我们开始讲讲这里的错误···巴拉巴拉一大堆,好啦,这里的错误很多,希望大家注意。我们下次再见。

        其实是我累了,敲了两个小时了,不想敲了。想继续看下去的,留言给我(虽然我不一定会看,我昨天看的留言还是,前年人家给我提问的)祝你好运。前面一篇有我QQ自己去找吧。

  • 4
    点赞
  • 7
    收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 1

打赏作者

peach_orange

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值