前言
對(duì)于OpenCV的豎屏檢測(cè)盯拱,網(wǎng)絡(luò)有很多盒发,多到百度排名前幾頁(yè),都是一大堆狡逢,但為什么要寫(xiě)這個(gè)文章迹辐,因?yàn)樗麄兊奈恼拢蟛糠侄际怯袉?wèn)題甚侣,或者是不可用的明吩,以下為他們的實(shí)現(xiàn)方法:
(1)隨便改改canvas繪制方向,并無(wú)其他操作
(2)使用WindowManager檢測(cè)屏幕旋轉(zhuǎn)方向殷费,修改canvas繪制方式
對(duì)于以上兩種做法印荔,實(shí)際是欠佳的低葫,改的是canvas,不是數(shù)據(jù)源仍律,識(shí)別過(guò)程還得橫屏才能識(shí)別嘿悬,難道寫(xiě)這些文章的人都沒(méi)發(fā)現(xiàn)?
方法
首先水泉,官方的Demo善涨,跑的是橫屏邏輯。而橫屏和豎屏最大的區(qū)別就是在于角度和長(zhǎng)寬比不一樣草则。核心原理钢拧,在于寬高的轉(zhuǎn)換,方向的改變炕横。
怎樣改源内?
首先,先對(duì)JavaCameraView代碼進(jìn)行修改份殿,核心改動(dòng)如下:
private class JavaCameraFrame implements CvCameraViewFrame {
@Override
public Mat gray() {
//返回Mat里的選定區(qū)域,這跟Yuv420sp格式緊密相關(guān)
//return mYuvFrameData.submat(0, mHeight, 0, mWidth);
//#Modified step3.1
Core.rotate(mYuvFrameData.submat(0, mHeight, 0, mWidth),
portrait_gray,Core.ROTATE_90_CLOCKWISE);
return portrait_gray;
}
@Override
public Mat rgba() {
if (mPreviewFormat == ImageFormat.NV21)
Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4);
else if (mPreviewFormat == ImageFormat.YV12)
Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGB_I420, 4); // COLOR_YUV2RGBA_YV12 produces inverted colors
else
throw new IllegalArgumentException("Preview Format can be NV21 or YV12");
//#Modified step3.2
Core.rotate(mRgba, portrait_rgba,Core.ROTATE_90_CLOCKWISE);
return portrait_rgba;
}
public JavaCameraFrame(Mat Yuv420sp, int width, int height) {
super();
mWidth = width;
mHeight = height;
//#Modified
portrait_mHeight=mWidth;
portrait_mWidth=mHeight;
portrait_gray=new Mat(portrait_mHeight,portrait_mWidth,CvType.CV_8UC1);
portrait_rgba=new Mat(portrait_mHeight,portrait_mWidth,CvType.CV_8UC4);
mYuvFrameData = Yuv420sp;
mRgba = new Mat();
}
public void release() {
mRgba.release();
}
private Mat mYuvFrameData;
private Mat mRgba;
private int mWidth;
private int mHeight;
//#Modified
private int portrait_mHeight;
private int portrait_mWidth;
private Mat portrait_gray;
private Mat portrait_rgba;
}
其實(shí)原理膜钓,就是通過(guò)Core.rotate()方法進(jìn)行旋轉(zhuǎn)。
第二個(gè)卿嘲,修改CameraBridgeViewBase的代碼颂斜,核心代碼如下:
/**
* This helper method can be called by subclasses to select camera preview size.
* It goes over the list of the supported preview sizes and selects the maximum one which
* fits both values set via setMaxFrameSize() and surface frame allocated for this view
*
* @param supportedSizes
* @param surfaceWidth
* @param surfaceHeight
* @return optimal frame size
*/
protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
//選擇一個(gè)相機(jī)frame大小
int calcWidth = 0;
int calcHeight = 0;
//允許的最大width和height
//#Modified step4
//相機(jī)Frame的mMaxWidth應(yīng)該與surface的surfaceHeight比
//相機(jī)Frame的mMaxHeight應(yīng)該與surface的surfaceWidth比
//int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth)? mMaxWidth : surfaceWidth;
//int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight)? mMaxHeight : surfaceHeight;
int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceHeight) ? mMaxWidth : surfaceHeight;
int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceWidth) ? mMaxHeight : surfaceWidth;
for (Object size : supportedSizes) {
int width = accessor.getWidth(size);
int height = accessor.getHeight(size);
//在允許的范圍內(nèi)選擇最大的size
//client是可通過(guò)設(shè)置小的mMaxWidth,mMaxHeight來(lái)選擇低分辨率frame的
if (width <= maxAllowedWidth && height <= maxAllowedHeight) {
if (width >= calcWidth && height >= calcHeight) {
calcWidth = (int) width;
calcHeight = (int) height;
}
}
}
return new Size(calcWidth, calcHeight);
}
/**
* This method shall be called by the subclasses when they have valid
* object and want it to be delivered to external client (via callback) and
* then displayed on the screen.
*
* @param frame - the current frame to be delivered
*/
protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
Mat modified;
if (mListener != null) {
//CvCameraViewListener2 mListener是client指定的
//這里調(diào)用客戶重載的接口方法且接收返回值
//這里都是在數(shù)據(jù)處理線程里執(zhí)行的
modified = mListener.onCameraFrame(frame);
} else {
//若client沒(méi)指定CvCameraViewListener2 mListener即client不準(zhǔn)備處理preview數(shù)據(jù)
//則modified設(shè)置為
//onPreviewFrame傳回的數(shù)據(jù)轉(zhuǎn)換成的rgba Mat
modified = frame.rgba();
}
//Log Mat的大小和Bitmap的大小
Log.d("FunnyAR", "mScale: " + mScale + " modified.rows: " + modified.rows()
+ " modified.cols: " + modified.cols() + " mCacheBitmap.getWidth(): " +
mCacheBitmap.getWidth() + " mCacheBitmap.getHeight() " +
mCacheBitmap.getHeight());
//標(biāo)志modified轉(zhuǎn)Bitmap是否成功
boolean bmpValid = true;
//若確實(shí)有modified則將其轉(zhuǎn)為Bitmap
if (modified != null) {
try {
Utils.matToBitmap(modified, mCacheBitmap);
} catch (Exception e) {
Log.e(TAG, "Mat type: " + modified);
Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
bmpValid = false;
}
}
//轉(zhuǎn)換成功通過(guò)畫(huà)布畫(huà)到surface里
if (bmpValid && mCacheBitmap != null) {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
if (BuildConfig.DEBUG)
Log.d(TAG, "mStretch value: " + mScale);
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);
}
if (mFpsMeter != null) {
mFpsMeter.measure();
mFpsMeter.draw(canvas, 20, 30);
}
getHolder().unlockCanvasAndPost(canvas);
}
}
}
通過(guò)修改獲取幀大小和幀分發(fā)的函數(shù),即可拾枣。具體原理就是寬高互換沃疮。
而對(duì)于前置攝像頭的方向,這里不再啰嗦了放前,原理一樣忿磅。
修改文件源碼:鏈接:https://pan.baidu.com/s/1OJs8IgV0TD6_Hq6nU1g9Xg
提取碼:tr4i
復(fù)制這段內(nèi)容后打開(kāi)百度網(wǎng)盤(pán)手機(jī)App,操作更方便哦
that's all-------------------------------------------------------------------------------------------