
隨著人工智能的快速發展以及終端設備硬件水平的不斷提升,在終端設備上直接運行智能係統成為可能,端側智能具備低延時,隱私安全等特點。同時降低了雲端智能存在的網絡傳輸不可靠風險,使得端側智能越來越得到重視。端側智能比較成熟的領域就是NLP以及CV。在CV領域OpenCV作為開源且強大的跨平台計算機視覺庫,在圖像處理以及圖像識別方向得到了廣泛應用。但是在Android平台OpenCV官方SDK在圖像預覽方面存在諸多缺陷。
SmartOpenCV是一个OpenCV在Android端的增强库,解决了OpenCV Android SDK在图像预览方面存在的诸多问题,而且无需修改OpenCV SDK源码,与OpenCV的SDK解耦,只需替换xml中原OpenCV的JavaCameraView / JavaCamera2View即可達到具備OpenCV官方SDK的原功能以及SmartOpenCV的增強功能。
OpenCV Android端SDK雖然很容易上手和使用,但是預覽存在很多問題,常見問題如下:
默認橫屏顯示,且無法通過接口修改預覽方向
预览绘制存在黑边:OpenCV默认绘制算法在绘制预览帧图像到Canvas时存在一定的偏移,在视觉上表现就是预览帧只会占SurfaceView控件的一部分区域,偏移部分区域会显示为黑色
if ( mScale != 0 ) {
canvas . drawBitmap ( mCacheBitmap , new Rect ( 0 , 0 , mCacheBitmap . getWidth (), mCacheBitmap . getHeight ()),
new Rect (( int ) (( canvas . getWidth () - mScale * mCacheBitmap . getWidth ()) / 2 ),
( int ) (( canvas . getHeight () - mScale * mCacheBitmap . getHeight ()) / 2 ),
( int ) (( canvas . getWidth () - mScale * mCacheBitmap . getWidth ()) / 2 + mScale * mCacheBitmap . getWidth ()),
( int ) (( canvas . getHeight () - mScale * mCacheBitmap . getHeight ()) / 2 + mScale * mCacheBitmap . getHeight ())), null );
} else {
canvas . drawBitmap ( mCacheBitmap , new Rect ( 0 , 0 , mCacheBitmap . getWidth (), mCacheBitmap . getHeight ()),
new Rect (( canvas . getWidth () - mCacheBitmap . getWidth ()) / 2 ,
( canvas . getHeight () - mCacheBitmap . getHeight ()) / 2 ,
( canvas . getWidth () - mCacheBitmap . getWidth ()) / 2 + mCacheBitmap . getWidth (),
( canvas . getHeight () - mCacheBitmap . getHeight ()) / 2 + mCacheBitmap . getHeight ()), null );
}预览帧大小选择算法不符合实际场景要求:对于预览帧大小的选择,OpenCV默认算法是选择小于预览控件(或设置的最大帧大小)的最大预览,这将导致在很多情况下预览图像的显示不能鋪滿整個控件甚至遠小於控件大小, 在絕大部分業務場景下,這種算法不能滿足實際需求
protected Size calculateCameraFrameSize ( List <?> supportedSizes , ListItemAccessor accessor , int surfaceWidth , int surfaceHeight ) {
int calcWidth = 0 ;
int calcHeight = 0 ;
int maxAllowedWidth = ( mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth )? mMaxWidth : surfaceWidth ;
int maxAllowedHeight = ( mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight )? mMaxHeight : surfaceHeight ;
for ( Object size : supportedSizes ) {
int width = accessor . getWidth ( size );
int height = accessor . getHeight ( size );
Log . d ( TAG , "trying size: " + width + "x" + height );
if ( width <= maxAllowedWidth && height <= maxAllowedHeight ) {
if ( width >= calcWidth && height >= calcHeight ) {
calcWidth = ( int ) width ;
calcHeight = ( int ) height ;
}
}
}
if (( calcWidth == 0 || calcHeight == 0 ) && supportedSizes . size () > 0 )
{
Log . i ( TAG , "fallback to the first frame size" );
Object size = supportedSizes . get ( 0 );
calcWidth = accessor . getWidth ( size );
calcHeight = accessor . getHeight ( size );
}
return new Size ( calcWidth , calcHeight );
}易使用:如果你项目中之前使用的是OpenCV的官方SDK,那么引入SmartOpenCV后只需将xml文件中的JavaCameraView / JavaCamera2View替换为SmartOpenCV的CamerPreview / Camera2Preview即可达到与使用官方SDK相同的效果
功能增强:
提供更友好的API接口:在继承OpenCV官方接口的同时,SmartOpenCV将众多繁杂操作统一通过CameraConfiguration来配置,提供更友好的Fluent API接口,让开发者能够更灵活的控制预览显示相关参数与配置
不直接依赖官方SDK,方便升级官方SDK :与OpenCV官方SDK解耦,只要官方SDK内部核心逻辑未做修改,那么SmartOpenCV可以兼容所有版本的官方SDK,使用SmartOpenCV后如果以后打算升级依赖的OpenCV为更新版本,只需將OpenCV的依賴更新為新版本即可,代碼無需做任何改動
| 橫屏 | 豎屏 | |
|---|---|---|
| OpenCV | 即使寬與高都設置為match_parent也無法全屏,存在黑邊 ![]() | 存在黑邊,且默認不支持豎屏 ![]() |
| SmartOpenCV | ![]() | ![]() |
smartopencv-app-debug.apk
opencv-app-debug.apk
Step1:在項目根目錄的build.gradle中添加對jitpack倉庫的配置
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Step2:在需要使用SmartOpenCV库的模块中添加依赖
dependencies {
implementation('com.github.HuTianQi:SmartOpenCV:1.0.1') { // 版本号建议使用已release的最新版本
exclude module: 'openCVLibrary411' // 由于目前多模块依赖时jitpack打包存在bug,排除打包时依赖的该模块
}
}
在项目中需要使用预览的xml中用SmartOpenCV的CameraPreview / Camera2Preview替换OpenCV的JavaCameraView / JavaCamera2View即可,就这么简单,其余的什么都不用做
< LinearLayout xmlns : android = " http://schemas.android.com/apk/res/android "
android : layout_width = " match_parent "
android : layout_height = " match_parent " >
<!-- <org.opencv.android.JavaCameraView -->
<!-- android:id="@+id/fd_activity_surface_view" -->
<!-- android:layout_width="match_parent" -->
<!-- android:layout_height="match_parent" /> -->
< tech .huqi.smartopencv.core.preview.CameraPreview
android : id = " @+id/fd_activity_surface_view "
android : layout_width = " match_parent "
android : layout_height = " match_parent " />
</ LinearLayout >如果打算通过SmartOpenCV提供的接口来更灵活的控制预览显示相关参数与配置,那么调用SmartOpenCV.getInstance().init()传入前面获取的预览控件对象即可,用法如下:
SmartOpenCV . getInstance (). init ( mOpenCvCameraView , new CameraConfiguration . Builder ()
. debug ( true )
. cameraIndex ( 0 ) // 设置摄像头索引,主要用于多摄像头设备,优先级低于frontCamera
. keepScreenOn ( false ) // 是否保持屏幕常亮
. frontCamera ( true ) // 是否使用前置摄像头
. openCvDefaultDrawStrategy ( false ) // 是否使用OpenCV默认的预览图像绘制策略
. openCvDefaultPreviewCalculator ( false ) // 是否使用OpenCV默认的预览帧大小计算策略
. landscape ( false ) // 是否横屏显示
. enableFpsMeter ( true ) // 开启预览帧率的显示
. usbCamera ( false ) // 是否使用USB摄像头,当设备接入的是USB摄像头时将其设置为true
. maxFrameSize ( 400 , 300 ) // 设置预览帧的最大大小
. cvCameraViewListener ( this ) // 设置OpenCV回调监听器
. previewSizeCalculator ( new IPreviewSizeCalculator () { // 自定义预览帧大小计算策略
@ Override
public Size calculateCameraFrameSize ( List < Size > supportedSizes , int surfaceWidth , int surfaceHeight ) {
// 若需要根据自己的具体业务场景改写览帧大小,覆写该方法逻辑
return new Size ( 1080 , 1920 );
}
})
. drawStrategy ( new IDrawStrategy () { // 自定义绘制策略
@ Override
public void drawBitmap ( Canvas canvas , Bitmap frameBitmap , int surfaceWidth , int surfaceHeight ) {
// 若需根据自己的具体业务场景绘制预览帧图像,覆写该方法逻辑
}
})
. build ());LICENSE
