图片转换颜色在预览框与NV21到JPEG

时间:2022-12-29 21:16:53

For nearly 15 days I 'm stuck on the recording of frames in the event onPreviewFrame Android camera . My goal is very simple, I want to record five consecutive images at the click of a button. Only I do not want to block the preview of the camera , so I use the previewFrame . But during the recording of my image in jpeg on the sd card it is green and pink with many horizontal bars. A term I will send a list of byte [] on a Web API.

近15天来,我一直被困在Android摄像头的事件记录中。我的目标很简单,我想在点击一个按钮时记录下五个连续的图像。只是我不想阻止相机的预览,所以我使用了previewFrame。但在我用jpeg在sd卡上录制图像时,它是绿色和粉红色的,有许多横条。我将在Web API上发送一个字节[]列表。

I already do a lot of research , trying to change the PictureSize and PreviewSize but nothing changes .

我已经做了很多研究,试图改变图片大小和预览大小,但是没有任何改变。

Another option would it not go through recording a video and extract the frame of it ?

另一个选择是它不通过录制视频并提取它的帧吗?

Here is the code in my fragment :

这是我片段中的代码:

package rocketweb.com.videocatcher.fragment;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.Toast;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import rocketweb.com.videocatcher.R;
import rocketweb.com.videocatcher.utilities.ImageHelper;

import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

/**
 * Created by Bastien on 15/03/2015.
 */
public class CameraFragment extends BaseFragment  {

    private Camera mCamera;
    private CameraPreview mPreview;
    private View mCameraView;
    private List<byte[]> imagesFrame;
    private boolean activeFrameCapture = false;

    public CameraFragment(){
        super();
    }

    public static CameraFragment newInstance(int sectionNumber) {
        CameraFragment fragment = new CameraFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_video_catcher, container, false);
        boolean opened = safeCameraOpenInView(view);

        if(opened == false){
            Log.d("Camera", "Error, Camera failed to open");
            return view;
        }

        Button captureButton = (Button) view.findViewById(R.id.button_capture);
        captureButton.setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        activeFrameCapture = !activeFrameCapture;
                    }
                }
        );

        imagesFrame = new ArrayList<byte[]>(){};
        return view;
    }

    private boolean safeCameraOpenInView(View view) {
        boolean qOpened = false;
        releaseCameraAndPreview();
        mCamera = getCameraInstance();
        mCameraView = view;
        qOpened = (mCamera != null);

        if(qOpened == true){
            mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera,view);
            FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_preview);
            preview.addView(mPreview);
            mPreview.startCameraPreview();
        }
        return qOpened;
    }

    public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open(); // attempt to get a Camera instance
        }
        catch (Exception e){
            e.printStackTrace();
        }
        return c; // returns null if camera is unavailable
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        releaseCameraAndPreview();
    }

    private void releaseCameraAndPreview() {

        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        if(mPreview != null){
            mPreview.destroyDrawingCache();
            mPreview.mCamera = null;
        }
    }

    class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

        private SurfaceHolder mHolder;
        private Camera mCamera;
        private Context mContext;
        private Camera.Size mPreviewSize;
        private List<Camera.Size> mSupportedPreviewSizes;
        private List<String> mSupportedFlashModes;
        private List<Camera.Size> mSupportedSizePicture;
        private View mCameraView;

        public CameraPreview(Context context, Camera camera, View cameraView) {
            super(context);

            // Capture the context
            mCameraView = cameraView;
            mContext = context;
            setCamera(camera);

            // Install a SurfaceHolder.Callback so we get notified when the
            // underlying surface is created and destroyed.
            mHolder = getHolder();
            mHolder.addCallback(this);
            mHolder.setKeepScreenOn(true);
            // deprecated setting, but required on Android versions prior to 3.0
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                public void onPreviewFrame(byte[] data, Camera camera) {
                    if (activeFrameCapture && imagesFrame.size() <= 5) {
                        Camera.Parameters parameters = camera.getParameters();
                        Camera.Size size = parameters.getPreviewSize();
                        YuvImage im = new YuvImage(data, ImageFormat.NV21, size.width,size.height, null);
                        Rect r = new Rect(0,0,size.width,size.height);
                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                        im.compressToJpeg(r, parameters.getJpegQuality(), baos);

                        try{
                            FileOutputStream output = new FileOutputStream(String.format(
                                    "/sdcard/%s_%d.jpg", "test", System.currentTimeMillis()));
                            output.write(baos.toByteArray());
                            output.flush();
                            output.close();
                        }catch(FileNotFoundException e)
                        {
                            e.printStackTrace();
                        }catch(IOException e){
                            e.printStackTrace();
                        }

                        imagesFrame.add(data);
                    }
                }
            });

        }

        public void startCameraPreview()
        {
            try{
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
            }
            catch(Exception e){
                e.printStackTrace();
            }
        }

        private void setCamera(Camera camera)
        {
            mCamera = camera;
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes();
            mSupportedSizePicture = mCamera.getParameters().getSupportedPictureSizes();

            Camera.Parameters parameters = mCamera.getParameters();

            // Set the camera to Auto Flash mode.
            if (mSupportedFlashModes != null && mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO))
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);

            mCamera.setParameters(parameters);
            requestLayout();
        }

        public void surfaceCreated(SurfaceHolder holder) {
            try {
                mCamera.setPreviewDisplay(holder);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            if (mCamera != null){
                mCamera.setPreviewCallback(null);
                mCamera.stopPreview();
                mCamera.release();
            }
        }

        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            // If your preview can change or rotate, take care of those events here.
            // Make sure to stop the preview before resizing or reformatting it.

            if (mHolder.getSurface() == null){
                // preview surface does not exist
                return;
            }

            // stop preview before making changes
            try {
                Camera.Parameters parameters = mCamera.getParameters();

                // Set the auto-focus mode to "continuous"
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

                // Preview size must exist.
                if(mPreviewSize != null) {
                    // 0 = landscape
                    // 90 = portrait

                    int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
                    int degrees = 0;
                    switch (rotation) {
                        case Surface.ROTATION_0: degrees = 90; break;
                        case Surface.ROTATION_90: degrees = 0; break;
                    }

                    mCamera.setDisplayOrientation(degrees);
                    if(degrees == 0)
                        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
                    else
                        parameters.setPreviewSize(mPreviewSize.height, mPreviewSize.width);

                }

                mCamera.setParameters(parameters);
                mCamera.startPreview();
            } catch (Exception e){
                e.printStackTrace();
            }
        }

        /**
         * Calculate the measurements of the layout
         * @param widthMeasureSpec
         * @param heightMeasureSpec
         */
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            // Source: http://*.com/questions/7942378/android-camera-will-not-work-startpreview-fails
            final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
            final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
            setMeasuredDimension(width, height);

            if (mSupportedPreviewSizes != null){
                mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
            }
        }

        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom)
        {
            // Source: http://*.com/questions/7942378/android-camera-will-not-work-startpreview-fails
            if (changed) {
                final int width = right - left;
                final int height = bottom - top;

                int previewWidth = width;
                int previewHeight = height;

                if (mPreviewSize != null){
                    Display display = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

                    switch (display.getRotation())
                    {
                        case Surface.ROTATION_0:
                            previewWidth = mPreviewSize.height;
                            previewHeight = mPreviewSize.width;
                            mCamera.setDisplayOrientation(90);
                            break;
                        case Surface.ROTATION_90:
                            previewWidth = mPreviewSize.width;
                            previewHeight = mPreviewSize.height;
                            break;
                        case Surface.ROTATION_180:
                            previewWidth = mPreviewSize.height;
                            previewHeight = mPreviewSize.width;
                            break;
                        case Surface.ROTATION_270:
                            previewWidth = mPreviewSize.width;
                            previewHeight = mPreviewSize.height;
                            mCamera.setDisplayOrientation(180);
                            break;
                    }
                }

                final int scaledChildHeight = previewHeight * width / previewWidth;
                mCameraView.layout(0, height - scaledChildHeight, width, height);
            }
        }

        private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int width, int height)
        {
            // Source: http://*.com/questions/7942378/android-camera-will-not-work-startpreview-fails
            Camera.Size optimalSize = null;

            final double ASPECT_TOLERANCE = 0.1;
            double targetRatio = (double) height / width;

            // Try to find a size match which suits the whole screen minus the menu on the left.
            for (Camera.Size size : sizes){

                if (size.height != width) continue;
                double ratio = (double) size.width / size.height;
                if (ratio <= targetRatio + ASPECT_TOLERANCE && ratio >= targetRatio - ASPECT_TOLERANCE){
                    optimalSize = size;
                }
            }

            // If we cannot find the one that matches the aspect ratio, ignore the requirement.
            if (optimalSize == null) {
                // TODO : Backup in case we don't get a size.
            }

            return optimalSize;
        }

    }
}

The layout of my fragment :

片段的布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <FrameLayout
        android:id="@+id/camera_preview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        />


    <RelativeLayout
        android:id="@+id/innerRelativeLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true" >
        <Button
            android:id="@+id/button_capture"
            android:text="Capture"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:layout_margin="10dp"
            android:layout_gravity="bottom"
            />
    </RelativeLayout>
</RelativeLayout>

My project configuration :

我的项目配置:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "rocketweb.com.videocatcher"
        minSdkVersion 15
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    packagingOptions {
        exclude 'META-INF/DEPENDENCIES.txt'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/LICENSE.txt'
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'
    compile 'com.google.code.gson:gson:2.3.1'
    compile "org.apache.httpcomponents:httpmime:4.2.3"
}

End an exemple of image result : DropBox link to pictures

最后以图像结果为例:DropBox链接到图片

I can upload android studio project if needed. Thank you in advance for your help

如果需要,我可以上传android studio项目。事先谢谢你的帮助

1 个解决方案

#1


0  

I think your problem might be when you try to setPreviewSize() according to the rotation of the camera. Please make sure that you set it to a supported preview size contained in parameters.getSupportedPreviewSizes() without swapping the dimensions.

我认为您的问题可能是当您试图根据摄像机的旋转来设置previewsize()时。请确保将其设置为参数中所包含的支持的预览大小。getsupportedpreviewsize()不需要交换维度。

I suspect your problem is that you are trying to swap a supported (width,height) pair and setting it to the camera and it's being rejected as a capture parameter, but it still being improperly used when telling the new YuvImage(...) constructor how to interpret the image byte array.

我怀疑您的问题是,您正在尝试交换一个受支持的(宽度、高度)对,并将其设置为相机,它被拒绝作为捕获参数,但是在告诉新的YuvImage(…)构造函数如何解释图像字节数组时,它仍然被不正确地使用。

#1


0  

I think your problem might be when you try to setPreviewSize() according to the rotation of the camera. Please make sure that you set it to a supported preview size contained in parameters.getSupportedPreviewSizes() without swapping the dimensions.

我认为您的问题可能是当您试图根据摄像机的旋转来设置previewsize()时。请确保将其设置为参数中所包含的支持的预览大小。getsupportedpreviewsize()不需要交换维度。

I suspect your problem is that you are trying to swap a supported (width,height) pair and setting it to the camera and it's being rejected as a capture parameter, but it still being improperly used when telling the new YuvImage(...) constructor how to interpret the image byte array.

我怀疑您的问题是,您正在尝试交换一个受支持的(宽度、高度)对,并将其设置为相机,它被拒绝作为捕获参数,但是在告诉新的YuvImage(…)构造函数如何解释图像字节数组时,它仍然被不正确地使用。