OpenCV-Android: хранение изображения с использованием JavaCameraView по клику приводит к очень низкому качеству изображения и неправильной ориентации - PullRequest
0 голосов
/ 19 апреля 2019

Я пытаюсь использовать OpenCV в моем приложении для Android.

Что должно произойти, изображение должно быть захвачено с помощью OpenCV (JavaCameraView) одним нажатием кнопки и сохранено на SD-карту.В то время как изображение, которое отображается в приложении (не изображение, а кадры выглядят хорошо), но при сохранении изображения, кажется, очень и очень низкого качества.

Я использовал основной способсоздание нового класса с расширением JavaCameraView и реализация PictureCallback.

Вот мой код:

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>

Это снимок экрана с изображением, которое необходимо захватить: Screenshot

Thisфактическое изображение получено: enter image description here

Низкое качество изображения из-за низкого разрешения?Если да, как изменить разрешение по умолчанию?

Также, как видно на скриншоте, камера показана только на среднем экране.Я попытался повернуть 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 градусов?

...