首页 » 软件开发 » 项目分享|基于ELF 1开发板的车牌识别系统(车牌识别开发板编译摄像头)

项目分享|基于ELF 1开发板的车牌识别系统(车牌识别开发板编译摄像头)

萌界大人物 2024-07-23 23:19:08 0

扫一扫用手机浏览

文章目录 [+]

一车牌识别的实现方法

1、车牌识别平台简介

本次车牌识别的实现方案是通过百度智能云平台进行实现,具体实现方法如下:

项目分享|基于ELF 1开发板的车牌识别系统(车牌识别开发板编译摄像头) 软件开发
(图片来自网络侵删)

进入百度智能云网页- > 选择文字识别- > 车牌识别

进入车牌识别页面之后可通过阅读技术文档来学习车牌识别的使用方法。

2、安装 OpenSSL

因为百度智能云是通过libcurl的https进行访问,而https的访问需要openSSL的支持,所以先编译OpenSSL。

wget https://www.openssl.org/source/openssl-1.1.1a.tar.gztar xvf openssl-1.1.1a.tar.gz./configmakesudo make install

3、安装curl

wget https://curl.se/download/curl-7.71.1.tar.bz2tar -xjf curl-7.71.1.tar.bz2cd curl-7.71.1/./configure --prefix=$PWD/_INSTALL_ARM --host=arm-linux-gnueabihf --with-openssl#./configure --prefix=$PWD/_INSTALL_GCC --with-openssl 为了在本地运行用GCC编译make make install

4、车牌识别过程

(在做本次步骤之前请先去阅读百度智能云车牌识别的使用方法)

在本地实现之前可通过平台提供的在线验证方法进行验证,如下图,需要在旁边输入access_token(通过阅读文档可知怎么获取)和一张车牌图片的base64 编码的字符串即可进行在线识别。

本地实现车牌识别的方法需要将识别代码拷贝到本地,并需要实现一个将图片转换为base64编码的函数,详细代码如下:

#include <stdio.h>#include <iostream>#include <string.h>#include <curl/curl.h>#include <json/json.h>#include <fstream>#include <memory>#include <cstdlib>#include <regex>#include <string>#include <unistd.h> inline size_t onWriteData(void buffer, size_t size, size_t nmemb, void userp){ std::string str = dynamic_cast<std::string >((std::string )userp); str->append((char )buffer, size nmemb); return nmemb;} std::string getFileBase64Content(const char path, bool urlencoded=false){ const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; std::string ret; int i = 0; int j = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; unsigned int bufferSize = 1024; unsigned char buffer[bufferSize]; std::ifstream file_read; file_read.open(path, std::ios::binary); while (!file_read.eof()){ file_read.read((char ) buffer, bufferSize sizeof(char)); int num = file_read.gcount(); int m = 0; while (num--){ char_array_3[i++] = buffer[m++]; if(i == 3){ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) ret += base64_chars[char_array_4[i]]; i = 0; } } } file_read.close(); if(i){ for(j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; while((i++ < 3)) ret += '='; } if (urlencoded) ret = curl_escape(ret.c_str(), ret.length()); return ret;} std::string performCurlRequest(const char pic_path, const std::string &token) { std::string result; char web_curl = nullptr; CURL curl = curl_easy_init(); CURLcode res; if (!asprintf(&web_curl, "https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate?access_token=%s", token.c_str())) { perror("asprintf error"); } if (curl) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curl, CURLOPT_URL, web_curl); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https"); struct curl_slist headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded"); headers = curl_slist_append(headers, "Accept: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); std::string base64_image = getFileBase64Content(pic_path, true); std::string post_data = "image=" + base64_image + "&multi_detect=false&multi_scale=false"; curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, onWriteData); if(curl_easy_perform(curl) != CURLE_OK) fprintf(stderr, "Curl request failed: %s\n", curl_easy_strerror(res)); } curl_easy_cleanup(curl); free(web_curl); return result;} int main(int argc, char argv[]) { std::string access_token = "填自己的access_tocken"; std::string result = performCurlRequest("./车牌图片.jpg", access_token); std::cout << result << std::endl; std::string json = result; std::regex pattern("\"number\":\"(.?)\""); std::smatch match; if (std::regex_search(json, match, pattern)) { std::cout << "read car number is:" << match[1].str() << std::endl; } return 0;}

编译

gcc demoCar.c -I ./curl-7.71.1/_INSTALL_GCC/include/ -L ./curl-7.71.1/_INSTALL_GCC/lib/ -l curl

编译完成将文件通过scp拷贝到ELF 1开发板进行运行即可,这样就可以将本地的车牌图片通过HTTPS发送到百度智能云进行识别,并将识别结果返回完成车牌识别。

注意:这里运行时可能会出现CA证书验证失败

root@ELF1:~# ./a.outOK:60

二移植 mjpg-streamer

在前面一个章节实现了对本地车牌图片的的识别,那如果需要通过摄像头进行车牌识别就需要借助 mjpg-streamer来实现,采用USB摄像头进行识别。

关于什么是 mjpg-streamer 就不解释了,大家可以自行查阅资料进行了解,这里只介绍一下 mjpg-streamer 移植到 ELF 1开发板的过程。

1、编译 jpeg

(1)下载 jpeg 源码压缩包

网址:http://www.ijg.org/files/

(2) tar -xvf jpegsrc.v8b.tar.gz

(3)编译配置

cd jpeg-8d./configure --prefix=$PWD/_INSTALL --host=arm-linux-gnueabihfmake -j8make install

2、编译 mjpg-streamer

(1)下载 mjpg-streamer 源码包

网址:https://sourceforge.net/projects/mjpg-streamer/

svn checkout https://svn.code.sf.net/p/mjpg-streamer/code/ mjpg-streamer-code

(2)tar -xvf mjpg-streamer.tar.gz

(3)配置

cd mjpg-streamer-code/mjpg-streamer/plugins/input_uvcvim Makefile

打开 Makefile 文件按照下图进行修改:

(4)编译 mjpg-streamer

因为mjpg-streamer默认是用GCC进行编译,所以要先将GCC改成自己的交叉编译工具,先安装需要用到的库。

sudo apt install graphicsmagick-imagemagick-compatsudo apt install imagemagick-6.q16sudo apt install imagemagick-6.q16hdri

更改GCC有两种方法:

方法一:

cd ~/mjpg-streamer-code/mjpg-streamermake CC=arm-linux-gnueabihf-gcc

方法二:

find -name "Makefile" -exec sed -i "s/CC = gcc/CC = arm-linux-gnueabihf-gcc/g" {} \;grep "arm-linux-gnueabihf-gcc" -nR

搜索当前目录及其子目录下的所有Makefile文件,并将Makefile里的CC变量设置为arm-linux-gnueabihf-gcc。
(注:arm-linux-gnueabihf-gcc 需要换成自己的交叉编译工具。

如下图所示所有目录下的Makefile中的CC都等于设置的交叉编译工具。

做完上面这些步骤之后编译代码:

make -j8

编译完成后会生成下图文件 :

.so :动态库

mjpg_streamer:提供可执行命令www:摄像头输出的网页

(5)移植到ELF 1开发板

scp -r mjpg-streamer/ root@192.168.0.106:~

(6)验证功能

登录ELF 1开发板,运行mjpg_streamer

cd mjpg-streamerexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/mjpg-streamer ./mjpg_streamer

当开发板运行mjpg_streamer成功后,在浏览器中输入开发板的IP地址和8080端口号,比如我的是192.168.0.106:8080,点击Stream选项就会出现摄像头中的实时画面,如下图所示。

这样就完成了mjpg_streamer 的移植,后续就可以mjpg_streamer实现一些具体的需求,比如打开摄像头视频:

mjpg_streamer -i "input_uvc.so -d /dev/video2 -f 30 -q 90 -n" -o "output_http.so -w /opt/www"

截取摄像头中的画面:

wget http://192.168.0.106:8080/?action=snapshot -O ./1.jpg

在这里就可以和前面车牌识别结合起来了,比如摄像头里面的画面是一张车牌信息,通过截取摄像头中的实时画面到本地,然后上传到百度智能云的后台进行识别,至此就完成通过摄像头进行车牌识别。

三Android APP的实现

Android APP 的实现很简单,主要功能就是将识别成功的车牌号在APP上面显示。
具体的实现方法是当ELF 1开发板成功识别车牌后,通过 Socket 将车牌发送到 Android APP 上面即可。
由于这部分代码比较简单,大致如下。

1、Android 端XML代码实现

<Button android:id="@+id/Z" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="license plate number" android:onClick="sendMessage" android:textColor="#ffffff" android:name=".MainActivity"/><TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30dp" android:textColor="#ffffff" android:layout_centerInParent="true"/>

XML 这部分只实现了两个功能,Button 用来显示车牌号的提示,TextView用来显示识别的车牌号。

2、 Android端Socket实现

private Handler handler;private TextView textView; @Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.text); handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bundle bundle = msg.getData(); String receivedMessage = bundle.getString("msg"); textView.setText(receivedMessage); } };} new Thread(new Runnable() { @Override public void run() { try { Socket client = new Socket("192.168.0.104", 8374); InputStream inputStream = client.getInputStream(); while (true) { byte[] data = new byte[128]; int len = inputStream.read(data); if (len > 0) { String str = new String(data, 0, len); Message message = new Message(); Bundle bundle = new Bundle(); bundle.putString("msg", str); message.setData(bundle); } } } catch (IOException e) { e.printStackTrace(); }}).start();

上面这段代码就实现了通过Socket接收来自开发板的车牌数据并将显示到TextView。

3、ELF 1开发板端实现

开发板端主要就是将识别成功的车牌号码通过Socket发送到 Android APP上面,代码如下:

int main(int argc, char argv[]) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { std::cerr << "Error creating socket" << std::endl; return 1; } struct sockaddr_in serv_addr; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr("192.168.0.104"); serv_addr.sin_port = htons(8374); if (bind(sockfd, (struct sockaddr)&serv_addr, sizeof(serv_addr)) < 0) return 1; if (listen(sockfd, 5) < 0) return 1; struct sockaddr_in cli_addr; socklen_t clilen = sizeof(cli_addr); int newsockfd = accept(sockfd, (struct sockaddr)&cli_addr, &clilen); if (newsockfd < 0) std::cerr << "Accept failed" << std::endl; const char reply = match[1].str().c_str(); int bytes_sent = send(newsockfd, reply, strlen(reply), 0); if (bytes_sent < 0) std::cerr << "Error sending data" << std::endl; close(newsockfd); close(sockfd); return 0;}

Android APP 部分就介绍结束,具体的运行界面效果如下图所示:

四总结

整个项目的识别过程如下图所示,首先运行程序,启动摄像头运行,然后会获取摄像头中的实时画面进行识别,识别成功就会将车牌的关键字检索出来上传到手机APP上面,这就是整个项目的关键运行流程。

(上述全部内容由ElfBoard的共创官提供,所有分享内容仅供学习交流使用,严禁任何商业用途。

标签:

相关文章

语言中的借用,文化交融的桥梁

自古以来,人类社会的交流与发展离不开语言的传播。在漫长的历史长河中,各民族、各地区之间的文化相互碰撞、交融,产生了许多独特的语言现...

软件开发 2025-01-01 阅读1 评论0

机顶盒协议,守护数字生活的新卫士

随着科技的飞速发展,数字家庭逐渐走进千家万户。在这个时代,机顶盒成为了连接我们与丰富多彩的数字世界的重要桥梁。而机顶盒协议,作为保...

软件开发 2025-01-01 阅读1 评论0

语言基础在现代社会的重要性及方法步骤

语言是人类沟通的桥梁,是社会发展的基础。语言基础作为语言学习的基石,对于个人、社会乃至国家的发展具有重要意义。本文将从语言基础在现...

软件开发 2025-01-01 阅读2 评论0

粤语电影,传承文化,点亮时代之光

粤语电影,作为中国电影产业的一朵奇葩,以其独特的地域特色、丰富的文化内涵和鲜明的艺术风格,赢得了广大观众的喜爱。本文将从粤语电影的...

软件开发 2025-01-01 阅读3 评论0

苹果游戏语言,塑造未来娱乐体验的基石

随着科技的飞速发展,游戏产业逐渐成为全球娱乐市场的重要支柱。在我国,游戏产业更是蓬勃发展,吸引了无数玩家和投资者的目光。而在这其中...

软件开发 2025-01-01 阅读1 评论0