OpenCV在Android上的应用示例
一.OpenCV介绍
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和MacOS操作系统上。它轻量级而且高效——由一系列C函数和少量C++类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
在移动端上使用OpenCV可以完成一系列图像处理的工作。
二.OpenCV在Android上的配置
我在项目中使用的OpenCV版本是4.x。
在AndroidStudio中创建一个Library,将官网下载的OpenCV导入后,就可以直接调用OpenCV中Java类的方法。
如果想调用C++的类,也可以使用CMake创建环境,然后通过include文件放入指定路径。
下面是项目中使用的CMakeLists.txt
cmake_minimum_required(VERSION3.6.0)
include_directories(
${CMAKE_SOURCE_DIR}/src/main/cpp/include
)
add_library(libopencv_java4SHAREDIMPORTED)
set_target_properties(
libopencv_java4
PROPERTIESIMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/libs/${ANDROID_ABI}/libopencv_java4.so)
add_library(libc++_sharedSHAREDIMPORTED)
set_target_properties(
libc++_shared
PROPERTIESIMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/libs/${ANDROID_ABI}/libc++_shared.so)
add_library(
detect
SHARED
src/main/cpp/detect-lib.cpp
src/main/cpp/detect-phone.cpp
)
find_library(
log-lib
log
)
target_link_libraries(
detectlibopencv_java4libc++_sharedjnigraphics
${log-lib}
)
其中,detect-lib.cpp和detect-phone.cpp是我创建的C++类。打成so文件时,会包含这2个类。
三.例子两则
3.1作为二维码识别的兜底方案
在Android原生开发中,二维码识别有老牌的zxing等开源库。为何还要使用OpenCV呢?
因为OpenCV有自己的优势,借助它可以定位到二维码的位置,一般识别不到二维码的内容大多是因为找不到它的位置。要是能够找到位置,就可以快速识别二维码的内容。
这样一来,识别二维码时需要先拍一张照,从图像中找出二维码的位置。当然,还可以对图像进行预处理,以便能够更好地找到二维码的位置。
下面的代码,展示了在应用层拍完照之后,将图片的路径传到jni层将其转换成对应的Mat对象,再转换成灰度图像,然后找出二维码的位置,要是能够找到的话就识别出二维码的内容。
extern"C"
JNIEXPORTjstringJNICALL
Java_com_xxx_sdk_utils_DetectUtils_qrDetect(JNIEnv*env,jclassjc,jstringfilePath){
constchar*file_path_str=env->GetStringUTFChars(filePath,0);
stringpath=file_path_str;
Matsrc=imread(path);
Matgray,qrcode_roi;
cvtColor(src,gray,COLOR_BGR2GRAY);
QRCodeDetectorqrcode_detector;
vectorpts;
stringdetect_info;
booldet_result=qrcode_detector.detect(gray,pts);
if(det_result){
detect_info=qrcode_detector.decode(gray,pts,qrcode_roi);
returnenv->NewStringUTF(detect_info.c_str());
}else{
detect_info="";
returnenv->NewStringUTF(detect_info.c_str());
}
}
对应的Java代码,方便应用层调用jni层的qrDetect()
publicclassDetectUtils{
static{
System.loadLibrary("detect");
}
/**
*识别二维码
*@paramfilePath
*@return
*/
publicstaticnativeStringqrDetect(StringfilePath);
......
}
最后是应用层的调用
//使用OpenCV进行二维码识别
valresult=DetectUtils.qrDetect(filePath)
L.d("opencvs识别二维码:$result")
3.2比对图像的差异
在我们的实际开发中遇到一个应用场景:需要判断我们的手机回收机里面是否存放了物体。(手机回收机是一个触摸屏设备,可以通过Android系统来操作内部的硬件设备。)
我们事先拍一张回收机内没有物体的图作为基准图像,等到需要判断是否存在物体时再拍一张图片。两幅图片对比看比例,比列超过阈值则认为回收机内存在着物体。
下面的代码,展示了在应用层拍完照之后,跟基准图片进行比对,并返回结果。
extern"C"
JNIEXPORTjbooleanJNICALL
Java_com_xxx_sdk_utils_DetectUtils_checkPhoneInMTA(JNIEnv*env,jclassjc,jstringbaseImgPath,jstringfilePath){
jbooleantRet=false;
constchar*file_path_str=env->GetStringUTFChars(filePath,0);
stringpath=file_path_str;
Matsrc=imread(path);
constchar*base_img_path_str=env->GetStringUTFChars(baseImgPath,0);
stringbasePath=base_img_path_str;
MatbaseImg=imread(basePath);
intresult=checkPhoneInBox(baseImg,src,40,0.1);
LOGI("checkPhoneInBoxresult=%d",result);
if(result==0){
tRet=true;
}
returntRet;
}
两张图片真正的比对是在checkPhoneInBox()中完成的。其中,maxFilter()是为了处理彩色的情况,然后使用高斯滤波进行降噪处理,再进行二值化处理,最后判断灰度差异区域占总图像的比列是否超过预先设定的阈值。
intcheckPhoneInBox(cv::MatbaseImg,cv::MatsnapImg,intdiffThresh,doublethreshRatio){
cv::MatbaseMaxImg,snapMaxImg,baseGausImg,snapGausImg;
if(baseImg.empty()||snapImg.empty())
{
return-1;
}
try{
maxFilter(baseImg,baseMaxImg);
maxFilter(snapImg,snapMaxImg);
}catch(...){
return-1;
}
cv::GaussianBlur(baseMaxImg,baseGausImg,cv::Size(5,5),0);
cv::GaussianBlur(snapMaxImg,snapGausImg,cv::Size(5,5),0);
cv::Matdiff,diffBin;
cv::MatnoMax;
cv::absdiff(baseGausImg,snapGausImg,diff);
cv::threshold(diff,diffBin,diffThresh,255,cv::THRESH_BINARY);
floatratio=(float)cv::countNonZero(diffBin)/(long)diffBin.total();
LOGI("ratio=%f,%d,%ld",ratio,cv::countNonZero(diffBin),(long)diffBin.total());
if(ratio>threshRatio)
{
return0;
}
else
{
return1;
}
}
intmaxFilter(cv::MatbaseImg,cv::Mat&maxImg)
{
if(baseImg.channels()<3)
{
maxImg=baseImg.clone();
}
else
{
maxImg.create(baseImg.size(),CV_8UC1);
for(intr=0;r(r,c);
maxTmp=(std::max)(s[0],s[1]);
maxTmp=(std::max)(maxTmp,s[2]);
maxImg.at(r,c)=maxTmp;
}
}
}
return0;
}
对应的Java代码,方便应用层调用jni层的checkPhoneInMTA()
publicclassDetectUtils{
static{
System.loadLibrary("detect");
}
/**
*判断MTA中是否有手机
*@parambaseImageFilePath基准的图片
*@paramfilePath拍摄的图片
*@return
*/
publicstaticnativebooleancheckPhoneInMTA(StringbaseImageFilePath,StringfilePath);
......
}
最后是应用层的调用
valresult=DetectUtils.checkPhoneInMTA(Constants.OPENCV_PHOTO_PATH,it.absolutePath)
四.总结
OpenCV是一款功能强大的图像处理库。但是它本身体积也较大,在移动端使用至少会增加AndroidApk包10M+的体积(主要取决于App要支持多少个CPU架构)。如果很介意的话,可以考虑自行裁剪OpenCV,然后再进行编译。
我所在的部门隶属于中台部门,主要输出接口和SDK。在SDK中使用OpenCV的确会给业务方造成困扰,未来也会考虑如何减少SDK的体积,以及把SDK做成模块化。
到此这篇关于OpenCV在Android上的应用示例的文章就介绍到这了,更多相关OpenCVAndroid应用内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!