0

I am trying to use OpenCV in my android application.

What should happen is an image should be captured using OpenCV (JavaCameraView) on a button click and saved to SD Card. While the image which is shown in the app (not image specifically, but frames look good), but on saving the image, it seems to be of a very very low quality.

I have used the basic way of creating a new class extending from JavaCameraView and implementing PictureCallback.

Here is my code:

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>

This is the screenshot of the image that is to be captured: Screenshot

This is the actual image captured: enter image description here

Is the low quality of the image because of low resolution? If yes, how to change the default resolution?

Also as it can be seen in the screenshot, camera is shown in only the middle screen. I tried turning android: orientation to landscape, but it moved the ImageView capturebtn too. I tried to set the params to FULLSCREEN but eevn it did not work:

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

For the final part, the camera view is actually 270 degrees rotated, I tried to do Core.Transpose, Imageproc.resize, and Core.flip operations, it works, but I am not sure if it works correctly, will resize distort the image? Also, will I have to do some operations in the OnPictureTaken method too to save the image after rotating it by 90 degrees?

Nisha
  • 397
  • 1
  • 5
  • 16
  • Do you ever set camera preview size and picture size? – Alex Cohn Apr 20 '19 at 04:38
  • Yes, thanks. I could do it now. I have another problem: How can we sort the MatOfPoint returned by findContour by its area? – Nisha Apr 21 '19 at 14:03
  • [1](https://stackoverflow.com/a/45057781/192373) [2](https://stackoverflow.com/a/40688407/192373) – Alex Cohn Apr 21 '19 at 15:09
  • Thanks again. And sorry to bother you again, but I was trying to find a way to get median of a Mat, but I couldn't find any such method. Can you please guide me if there's one. Thanks again. – Nisha Apr 21 '19 at 17:19
  • For mean, I found that Core.meanStdDev exists, but I don't know for median. Actually I am trying to do automaticCannyEdgeDetection [here](https://stackoverflow.com/questions/4292249/automatic-calculation-of-low-and-high-thresholds-for-the-canny-operation-in-open). I tried doing Otsu and mean, but then there is only 1 detected contour, which is the outermost boundary of camera itself. And so, I am stuck. – Nisha Apr 21 '19 at 18:26

0 Answers0