此次用到的所有资料我都放到了奶牛快传里,下载的话速度极快!
https://c-t.work/s/fe0b4a22171342
我买这个板子已经很久了,跟着野火正点原子的教程踉踉跄跄学了一段儿,对很多基础知识也是一知半解,最终也算是搞了下字符驱动开发,并且做了自己的第一次尝试:
【linux学习vlog】周末两天爆肝入门linux嵌入式
然后呢最近终于费了九牛二虎之力搞成了自己第一个最完善的应用:基于百度图像与语音的一个小作品:
【野火linux】我的第一个linux+QT应用
接下来就总结一下我的制作过程,由于我没有该视频的文档,所以我都是跟着视频敲代码,一点一点撸出来的!!
参考讯为的图像识别项目教程: https://www.bilibili.com/video/BV157411c7sc/
我的做法与讯为的不同点:
讯为使用的摄像头带驱动,我买的免驱的USB摄像头,这样更简单。
讯为调用摄像头采用的是Opencv,我感觉没必要,我直接用的QT的camera组件。
然后讯为在同步板子和电脑上交叉编译库的时候,用的是设计根文件系统,而我直接通过NFS怼进去的,这样更简单(野火构建根本文件系统的教程一言难尽,依赖文件太多,下载太慢。)
QT交叉编译环境的搭建 (按照野火的文档手册39.1节来就可以,前提是你要把野火需要下载的东西都搞定,且编译通过)
PC端QT环境的搭建,最终完成的效果是生成一个最基本的QT程序,能在PC上编译运行,然后交叉编译以后能板子上正常运行,这时候最基本的准备就完成了。
这里的坑就是:
*安装 arm-linux-gnueabihf-gcc v8.3.0*
野火文档上面的编译脚本是有问题的,一是他的排版问题,导致你sh命令总会出错,你可以自己拷贝下来以后自己排查一下修改一下,我编译高版本的8.3的GCC的时候就已经出问题了,一是sh脚本根本无法运行,二是下载失败。其实这个还是比较简单的,因为sh脚本并不复杂可以看得懂,就是把编译器下载下来,然后放到/opt目录下即可。
我已经准备好了在网盘里:arm-linux-gnueabihf-gcc v8.3.0
然后按照野火的教程走,导出环境变量,并验证版本。
剩下的交叉编译触摸驱动tslib以及声卡alsa(这两个是交叉编译进QT工程里面的)
交叉编译QT也是一样的,使用我检验过的即可。
可以按照我做好的sh脚本来执行,直接就OK了。(野火的排版有问题,复制下来是不行的)
然后就按照野火教程安装QT Creator 这里是没问题的
然后把交叉编译的第一个APP nfs扔到板子里,能正常跑,OK到这里基本的准备条件就完成了,就可以在QT的海洋里遨游了。
主要是用到百度的三元组
AppID
API Key
Secret Key
接下来其他的就暂时脱离QT了,这是对 整个图像识别和百度语音识别做准备的:
编译openssl
cd openssl-1.0.2f/
setarch i386 ./config no-asm shared --prefix=/usr/local/openssl/
CC= arm-linux-gnueabihf-gcc
AR= arm-linux-gnueabihf-ar $(ARFLAGS) r
RANLIB= arm-linux-gnueabihf-ranlib
NM= arm-linux-gnueabihf-nm
make
sudo make install
编译curl
./configure --prefix=/usr/local/curl/ --host=arm-linux-gnueabihf CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ --with-ssl=/usr/local/openssl
make
sudo make install
编译jsoncpp (我这里虽然编译成功了,但是在QT里总是调用失败,解决方法见下文)
mkdir arm_jsoncpp
cp -r include/ arm_jsoncpp/
cp src/lib_json/* arm_jsoncpp/
arm-linux-gnueabihf-g++ -c *.cpp -I./include -fPIC
mkdir lib
ar cr ./lib/libjsoncpp.a *.o
编译动态库
arm-linux-gnueabihf-g++ -shared -fPIC *.cpp -I./include -o ./lib/libjsoncpp.so
××解决jsoncpp问题××
我百度查了查 jsoncpp即使不进行动态编译,一样可以调用,直接把cjson需要用到的文件扔QT工程里面就行了。直接在QT里引用外部文件把整个json文件夹饮用即可!
参考链接: https://www.cnblogs.com/zengjfgit/p/4959965.html
然后就是以上依赖库的环境变量引导,把上面的文件在PC上放到指定位置进行交叉编译生成QT可执行文件,nfs扔到板子上运行,会提示找不到依赖库。
此时把依赖库文件,放到imx6ull板子的指定路径,并且编辑环境变量。再次运行就不报错了。
将文件放到开发板的对应位置以及lib文件夹
root@npi:/mnt# cp -r curl /usr/local/
root@npi:/mnt# cp -r openssl /usr/local/
root@npi:/mnt# cp -r curl/lib/* /lib/
root@npi:/mnt# cp -r openssl/lib/* /lib/
vi ./etc/init.d/rcS 如下图
讯为的教程使用的是 设计根文件系统,而我是直接NFS传进去,其实原理是一样的,无论怎么样,只要能让QT应用在板子的指定位置找到指定的依赖文件即可。
注:如果使用的是野火的debian系统找不到rcS文件,那么就用系统变量的配制方法:
vi .bashrc
在文末添加如下内容
export PATH=$PATH:/usr/local/openssl/bin
export PATH=$PATH:/usr/local/curl/bin
编辑完成以后 更新一下,然后查看是否OK
source ~/.bashrc
echo $PATH
百度APP初始化部分:
#include "json/include/json/json.h" //由于我没编译成功jsoncpp的依赖库,所以用这种本地依赖的方法来作
#include "ocr.h"
#include "speech.h"
// 设置语音合成 APPID/AK/SK
std::string speech_app_id = "21275374";
std::string speech_api_key = "5ZGKQxLfmVamG2GnP9zsXGKX";
std::string speech_secret_key = "ZW5Aoopu2K88DOQtgcZBQGjmTnDH8ncQ";
aip::Speech client_speech(speech_app_id, speech_api_key, speech_secret_key);
// 设置文字识别APPID/AK/SK
std::string app_id = "21267850";
std::string api_key = "mXTgSiRIQ07OYFGqB3aSlY2t";
std::string secret_key = "LWoGPMUaqKzLfUPihDUnphEMNX148uR2";
aip::Ocr client(app_id, api_key, secret_key);
车牌识别部分:
std::string GetPlateNumber(std::string PicturePath)
{
Json::Value result;
std::string image;
aip::get_file_content(PicturePath.c_str(), &image);
// 调用车牌识别
result = client.license_plate(image, aip::null);
// 如果有可选参数
std::map options;
options["multi_detect"] = "true";
// 带参数调用车牌识别
result = client.license_plate(image, options);
if(result["error_code"].isNull()) //无报错
{
return ("车牌号"+result["words_result"][0]["number"].asString()+"欢迎光临");
}else
{
return ("error");
}
}
语音合成(文字转语音部分):
int tts(std::string text,std::string FileName)
{
std::ofstream ofile;
std::string file_ret;
std::map options;
options["spd"] = "4";
options["per"] = "5";
// 合成成功的二进制数据写入文件中
ofile.open(FileName, std::ios::out | std::ios::binary);
// 不带可选参数调用
//Json::Value result = client_speech.text2audio("百度语音合成测试", aip::null, file_ret);
// 带可选参数调用, 参数参考参数列表中的可选参数
Json::Value result = client_speech.text2audio(text, options, file_ret);
// 如果file_ret为不为空则说明合成成功,返回mp3文件内容回结果
if (!file_ret.empty())
{
ofile << file_ret;
return 0;
} else {
// 服务端合成错误
std::cout << result.toStyledString();
return 1;
}
return 2;
}
拍照按钮事件的执行:
void Camera::processCapturedImage(int requestId, const QImage& img)
{
Q_UNUSED(requestId);
QImage scaledImage = img.scaled(ui->viewfinder->size(),
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
ui->lastImagePreviewLabel->setPixmap(QPixmap::fromImage(scaledImage));
const QPixmap *pixmap=ui->lastImagePreviewLabel->pixmap();
pixmap->save("./a.jpg"); //存储到指定位置
std::string out;
std::int8_t sp_out;
out = GetPlateNumber("./a.jpg");
ui->statusbar->showMessage(out.c_str());
//语音合成
sp_out = tts(out,"./a.mp3");
if(sp_out == 0)
{
//播放 开启新线程执行系统命令
QProcess *process = new QProcess();
process->start("gplay-1.0 a.mp3");
// system("gplay-1.0 a.mp3");
}else
{
ui->statusbar->showMessage("语音合成失败");
}
QMessageBox::warning(this, tr("OK"), out.c_str());
// Display captured image for 4 seconds. 这里会在linux板子上面卡住
// displayCapturedImage();
// QTimer::singleShot(4000, this, &Camera::displayViewfinder);
}
这一切搞完,就OK了,完美运行!我太难了!四处碰壁。
总之,最终完成了,还是很开心的。好好学习、天天向上!