Android OpenCV竖屏处理策略和底层代码修改
在Android上做机器视觉,不可避免地都会用到OpenCV for Android库,视频流的捕获和预览通常使用 JavaCamera2View 控件,但这个控件在横屏下运作良好,竖屏下问题很多。本文用最小代价解决了这个问题,并给出了关键源代码。...
在Android上做机器视觉,不可避免地都会用到OpenCV for Android库,视频流的捕获和预览通常使用 JavaCamera2View 控件,但这个控件在横屏下运作良好,竖屏下问题很多。
安卓手机只有在横屏下,其镜头传感器才是原始状态,竖屏需要进行旋转,否则看起来还是横的。
为了保持旋转不变性,最佳策略是采用正方形的预览区域,有些手机的镜头的尺寸不一定有正方形的输出尺寸,比如以vivo Y32为例,屏幕的分辨率是1600x720,镜头并没有720x720的输出,这种情况下我们可以找到一个最接近的960x720,然后将输出剪裁为720x720。
下面详细描述修改要点。
首先,修改 CameraBridgeViewBase.java,增加一个成员变量:
// 正方形边长,大于0表示竖屏,将屏幕显示区域改为正方形,方便任意旋转
public int mSideLengthSquare = 0; // 0则为横屏,不需要额外处理
这个判断在 JavaCamera2View.java文件完成:
protected boolean connectCamera(int width, int height) {
Log.i(LOGTAG, "PreviewSize(" + width + "x" + height + ")");
startBackgroundThread();
initializeCamera();
try {
// 判断是否竖屏的逻辑
if(width < height){ // 原始预览区域的宽度比高度小,说明是竖屏
mSideLengthSquare = width;
}
...
}
...
}
修改文件的CameraBridgeViewBase.java文件的calculateCameraFrameSize()函数:
if(mSideLengthSquare > 0){ // 如果是竖屏
// 找出最接近正方形边长的输出尺寸:
int minDx = 1024*100;
for (Object size : supportedSizes) {
int width = accessor.getWidth(size);
int height = accessor.getHeight(size);
Log.d(TAG, "trying size 0: " + width + "x" + height);
if(width >= mSideLengthSquare && height >= mSideLengthSquare){
int dx = (width - mSideLengthSquare) + (height - mSideLengthSquare);
if(dx < minDx){
calcWidth = (int) width;
calcHeight = (int) height;
minDx = dx;
}
}
}
}
else {
...
}
注意在调用计算尺寸后,还要将缩放比例设为0,我们正方形区域没必要缩放,如果不设置下面这个语句,将会缩小显示:
mScale = 0;
修改CameraBridgeViewBase.java文件的AllocateCache()函数,如忘记修改将会崩溃:
protected void AllocateCache()
{
Log.i(TAG, "AllocateCache size: " + mFrameWidth + "x" + mFrameHeight);
// mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
if(mSideLengthSquare > 0) { // 如果是竖屏,则输出的是正方形预览
mCacheBitmap = Bitmap.createBitmap(mSideLengthSquare, mSideLengthSquare, Bitmap.Config.ARGB_8888);
}
else{
mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
}
}
然后是修改CameraBridgeViewBase.java文件的deliverAndDrawFrame()函数:
protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
Mat modified;
if (mListener != null) {
modified = mListener.onCameraFrame(frame);
} else {
modified = frame.rgba();
}
if(mSideLengthSquare > 0) { // 将帧画面剪裁为正方形
int w0 = modified.cols();
int h0 = modified.rows();
if (w0 != mSideLengthSquare || h0 != mSideLengthSquare) {
int x0 = 0;
int y0 = 0;
if (w0 > mSideLengthSquare) {
x0 = (w0 - mSideLengthSquare) / 2;
w0 = mSideLengthSquare;
}
if (h0 > mSideLengthSquare) {
y0 = (h0 - mSideLengthSquare) / 2;
h0 = mSideLengthSquare;
}
org.opencv.core.Rect rect = new org.opencv.core.Rect(x0, y0, w0, h0);
modified = new Mat(modified, rect);
}
}
...
}
最后,则是你自己的C++代码了,别忘了将画面Mat旋转90度,这个是最容易的部分,就不贴代码了。
上述代码具有极大的通用性,不需要设置尺寸而取最大的正方形,是一个很好的策略。用很多手机测试过,效果很棒,CPU占用极小,可以保持30+的帧率不变。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)