首页 > 解决方案 > OpenCV-Android:单击时使用 JavaCameraView 存储图像会导致图像质量非常低和方向错误

问题描述

我正在尝试OpenCV在我的 android 应用程序中使用。

应该发生的是应该在单击按钮时使用 OpenCV (JavaCameraView) 捕获图像并将其保存到 SD 卡。虽然应用程序中显示的图像(不是专门的图像,但框架看起来不错),但在保存图像时,它的质量似乎非常低。

我使用了创建从JavaCameraViewPictureCallback 扩展和实现的新类的基本方法。

这是我的代码:

CamView.java

public class CamView extends JavaCameraView implements PictureCallback {

private static final String TAG = "Sample::CamView";
private String mPictureFileName;

public CamView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public List<String> getEffectList() {
    return mCamera.getParameters().getSupportedColorEffects();
}

public boolean isEffectSupported() {
    return (mCamera.getParameters().getColorEffect() != null);
}

public String getEffect() {
    return mCamera.getParameters().getColorEffect();
}

public void setEffect(String effect) {
    Camera.Parameters params = mCamera.getParameters();
    params.setColorEffect(effect);
    mCamera.setParameters(params);
}

public List<Size> getResolutionList() {
    return mCamera.getParameters().getSupportedPreviewSizes();
}

public void setResolution(Size resolution) {
    disconnectCamera();
    mMaxHeight = resolution.height;
    mMaxWidth = resolution.width;
    connectCamera(getWidth(), getHeight());
}

public Size getResolution() {
    return mCamera.getParameters().getPreviewSize();
}

public void takePicture(final String fileName) {
    Log.i(TAG, "Taking picture");
    this.mPictureFileName = fileName;
    // Postview and jpeg are sent in the same buffers if the queue is not empty when performing a capture.
    // Clear up buffers to avoid mCamera.takePicture to be stuck because of a memory issue
    mCamera.setPreviewCallback(null);

    // PictureCallback is implemented by the current class
    mCamera.takePicture(null, null, this);
}

@Override
public void onPictureTaken(byte[] data, Camera camera) {
    Log.i(TAG, "Saving a bitmap to file");
    // The camera preview was automatically stopped. Start it again.
    mCamera.startPreview();
    mCamera.setPreviewCallback(this);

    // Write the image in a file (in jpeg format)
    try {
        FileOutputStream fos = new FileOutputStream(mPictureFileName);

        fos.write(data);
        fos.close();

    } catch (java.io.IOException e) {
        Log.e("PictureDemo", "Exception in photoCallback", e);
    }

}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >


<com.nisha.scanner.CamView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone"
    android:id="@+id/main_activity_java_surface_view" />

<ImageView
    android:id="@+id/captureBtn"
    android:layout_width="64dp"
    android:layout_height="64dp"
    android:contentDescription="@string/app_name"
    android:src="@drawable/camcapture"
    android:layout_gravity="bottom|center"
    android:layout_marginBottom="20dp"/>

MainActivity.java

public class MainActivity extends Activity implements CvCameraViewListener2, OnTouchListener {
private static final String TAG = "OCVSample::Activity";

private CamView mOpenCvCameraView;
private List<Size> mResolutionList;
private MenuItem[] mEffectMenuItems;
private SubMenu mColorEffectsMenu;
private MenuItem[] mResolutionMenuItems;
private SubMenu mResolutionMenu;

private Mat mRgba, mRgbaT, mRgbaF;

private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
    @Override
    public void onManagerConnected(int status) {
        switch (status) {
            case LoaderCallbackInterface.SUCCESS:
            {
                Log.i(TAG, "OpenCV loaded successfully");
                mOpenCvCameraView.enableView();
           //     mOpenCvCameraView.setOnTouchListener(MainActivity.this);
            } break;
            default:
            {
                super.onManagerConnected(status);
            } break;
        }
    }
};

public MainActivity() {
    Log.i(TAG, "Instantiated new " + this.getClass());
}

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    Log.i(TAG, "called onCreate");
    super.onCreate(savedInstanceState);

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    setContentView(R.layout.activity_main);

    mOpenCvCameraView = (CamView) findViewById(R.id.main_activity_java_surface_view);

    mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);

    mOpenCvCameraView.setCvCameraViewListener(this);

    ImageView captureBtn = (ImageView)findViewById(R.id.captureBtn);

    captureBtn.setOnTouchListener(MainActivity.this);
}

@Override
public void onPause()
{
    super.onPause();
    if (mOpenCvCameraView != null)
        mOpenCvCameraView.disableView();
}

@Override
public void onResume()
{
    super.onResume();
    if (!OpenCVLoader.initDebug()) {
        Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
    } else {
        Log.d(TAG, "OpenCV library found inside package. Using it!");
        mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
    }
}

public void onDestroy() {
    super.onDestroy();
    if (mOpenCvCameraView != null)
        mOpenCvCameraView.disableView();
}

public void onCameraViewStarted(int width, int height) {
}

public void onCameraViewStopped() {
}

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    return inputFrame.rgba();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {

    super.onCreateOptionsMenu(menu);
    MenuInflater menuInflater = new MenuInflater(this);
    menuInflater.inflate(R.menu.camera_menu, menu);

    return true;
}

public boolean onOptionsItemSelected(MenuItem item) {
    Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
    if (item.getGroupId() == 1)
    {
        mOpenCvCameraView.setEffect((String) item.getTitle());
        Toast.makeText(this, mOpenCvCameraView.getEffect(), Toast.LENGTH_SHORT).show();
    }
    else if (item.getGroupId() == 2)
    {
        int id = item.getItemId();
        Size resolution = mResolutionList.get(id);
        mOpenCvCameraView.setResolution(resolution);
        resolution = mOpenCvCameraView.getResolution();
        String caption = Integer.valueOf(resolution.width).toString() + "x" + Integer.valueOf(resolution.height).toString();
        Toast.makeText(this, caption, Toast.LENGTH_SHORT).show();
    }

    return true;
}

@SuppressLint("SimpleDateFormat")
@Override
public boolean onTouch(View v, MotionEvent event) {
    Log.i(TAG,"onTouch event");
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
    String currentDateandTime = sdf.format(new Date());
    String fileName = Environment.getExternalStorageDirectory().getPath() +
            "/sample_picture_" + currentDateandTime + ".jpg";
    mOpenCvCameraView.takePicture(fileName);
    Toast.makeText(this, fileName + " saved", Toast.LENGTH_SHORT).show();
    return false;
}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.nisha.scanner">

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature
    android:name="android.hardware.camera"
    android:required="false" />
<uses-feature
    android:name="android.hardware.camera.autofocus"
    android:required="false" />
<uses-feature
    android:name="android.hardware.camera.front"
    android:required="false" />
<uses-feature
    android:name="android.hardware.camera.front.autofocus"
    android:required="false" />


<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:screenOrientation="landscape"
    >
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

</manifest>

这是要捕获的图像的屏幕截图: 截屏

这是捕获的实际图像: 在此处输入图像描述

图像质量低是因为分辨率低吗?如果是,如何更改默认分辨率?

此外,从屏幕截图中可以看出,相机仅显示在中间屏幕中。我尝试将 android: 方向转为横向,但它也移动了 ImageView capturebtn。我试图将参数设置为FULLSCREEN但它不起作用:

this.requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

对于最后一部分,相机视图实际上旋转了 270 度,我尝试做Core.Transpose, Imageproc.resize, 和Core.flip操作,它工作,但我不确定它是否工作正常,调整大小会扭曲图像吗?另外,我是否也必须在 OnPictureTaken 方法中进行一些操作才能将图像旋转 90 度后保存?

标签: androidimageopencvandroid-cameraopencv4android

解决方案


推荐阅读