CameraX 分析/相机 onPreviewFrame

CameraX Analysis / Camera onPreviewFrame

在CameraX Analysis中,setTargetResolution(new Size(2560, 800),但在Analyzer中imageProxy.getImage.getWidth=1280 and getHeight=400, and YUVToByte(imageProxy.getImage).length()=768000。在Camera中,parameter.setPreviewSize(2560, 800)然后byte[].length在onPreviewFrame中是3072000(等于768000*(2560/1280)*(800/400))。如何让CameraX Analyzer imageProxy.getImage.getWidth and getHeight = 2560 and 800, and YUVToByte(ImageProxy.getImage).length()=3072000? 在CameraX的onPreviewFrame()中,res总是=null,在Camera的onPreviewFrame()中,res可以得到currect值,有什么不同CameraX 和 Camera 之间的区别?在 CameraX 中我应该做什么?

CameraX:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);

    try {
        copyDataBase();
    } catch (IOException e) {
        e.printStackTrace();
    }
    getSupportActionBar().hide();
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

    viewFinder = findViewById(R.id.previewView);

    executor = Executors.newSingleThreadExecutor();

    if (!allPermissionGranted()) {
        ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
    }

    DisplayMetrics metric = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metric);
    width = metric.widthPixels;
    height = metric.heightPixels;
    l = 100;
    r = width - 100;
    ntmpH = (r - l) * 58 / 100;
    t = (height - ntmpH) / 2;
    b = t + ntmpH;

    double proportion = (double) width / (double) preHeight;
    double hproportion = (double) height / (double) preWidth;
    l = (int) (l / proportion);
    t = (int) (t / hproportion);
    r = (int) (r / proportion);
    b = (int) (b / hproportion);

    m_ROI[0] = l;
    m_ROI[1] = t;
    m_ROI[2] = r;
    m_ROI[3] = b;

    cameraProviderFuture = processCameraProvider.getInstance(this);
    cameraProviderFuture.addListener(() -> {
        try {
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
            @SuppressLint("RestrictedApi") Preview preview = new Preview.Builder().build();
            CameraSelector cameraSelector = new CameraSelector.Builder().
                    requireLensFacing(CameraSelector.LENS_FACING_BACK).build();

            ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                    .setTargetResolution(new Size(2560, 800))
                    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                    .setTargetRotation(Surface.ROTATION_90)
                    .build();


            imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() {
                @SuppressLint("UnsafeExperimentalUsageError")
                @Override
                public void analyze(@NonNull ImageProxy imageProxy) {
                    if (imageProxy.getFormat() == ImageFormat.YUV_420_888) {
                        image = imageProxy.getImage();
                        bIninKernal();
                        Log.d("Size ", image.getWidth() + "/" + image.getHeight());
                        onPreviewFrame(YUVToByte(image));
                    } else {
                        Log.d("Status ", "照片格式錯誤" + imageProxy.getFormat());
                    }
                    imageProxy.close();
                }

            });


            cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis);
            preview.setSurfaceProvider(viewFinder.createSurfaceProvider());
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
    }, ContextCompat.getMainExecutor(this));

}


@NonNull
@Override
public CameraXConfig getCameraXConfig() {
    return Camera2Config.defaultConfig();
}


private boolean allPermissionGranted() {
    for (String permission : REQUIRED_PERMISSIONS) {
        if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

private byte[] YUVToByte(Image image) {
    Image.Plane[] planes = image.getPlanes();

    ByteBuffer buffer0 = planes[0].getBuffer();
    ByteBuffer buffer1 = planes[1].getBuffer();
    ByteBuffer buffer2 = planes[2].getBuffer();

    int width = image.getWidth();
    int height = image.getHeight();

    byte[] data = new byte[image.getWidth() * image.getHeight() * ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8];
    byte[] rowData1 = new byte[planes[1].getRowStride()];
    byte[] rowData2 = new byte[planes[2].getRowStride()];

    int bytesPerPixel = ImageFormat.getBitsPerPixel(ImageFormat.YUV_420_888) / 8;
    // loop via rows of u/v channels
    int offsetY = 0;
    int sizeY = width * height * bytesPerPixel;
    int sizeUV = (width * height * bytesPerPixel) / 4;

    for (int row = 0; row < height; row++) {
        // fill data for Y channel, two row
        {
            int length = bytesPerPixel * width;
            buffer0.get(data, offsetY, length);
            if (height - row != 1)
                buffer0.position(buffer0.position() + planes[0].getRowStride() - length);
            offsetY += length;
        }
        if (row >= height / 2)
            continue;
        {
            int uvlength = planes[1].getRowStride();
            if ((height / 2 - row) == 1) {
                uvlength = width / 2 - planes[1].getPixelStride() + 1;
            }
            buffer1.get(rowData1, 0, uvlength);
            buffer2.get(rowData2, 0, uvlength);
            // fill data for u/v channels
            for (int col = 0; col < width / 2; ++col) {
                // u channel
                data[sizeY + (row * width) / 2 + col] = rowData1[col * planes[1].getPixelStride()];

                // v channel
                data[sizeY + sizeUV + (row * width) / 2 + col] = rowData2[col * planes[2].getPixelStride()];
            }
        }
    }
    return data;
}

private void bIninKernal() {
    api = new LPR();
    String FilePath = Environment.getExternalStorageDirectory().toString() + "/lpr.key";

    int nRet = api.Init(this, m_ROI[0], m_ROI[1], m_ROI[2], m_ROI[3], preHeight, preWidth, FilePath);
    if (nRet != 0) {
        bInitKernal = false;
        Log.d("Status ", "相機開啟失敗");
    } else {
        bInitKernal = true;
    }
}

private void onPreviewFrame(byte[] data) {
    bIninKernal();
    tackData = data;
    Log.d("data length ", data.length + "");
    resultStr = "";
    if (!leaving && bInitKernal) {
        byte[] result;
        String res = "";
        result = api.VideoRec(tackData, 1280, 400, 1);
        try {
            res = new String(result, "gb2312");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if (res != null && !"".equals(res.trim())) {
            resultStr = res.trim();
            if (resultStr != "") {
                leaving = true;
                MediaActionSound sound = new MediaActionSound();
                sound.play(MediaActionSound.SHUTTER_CLICK);
                Log.d("Status ", "辨識成功");
                Log.d("車牌號碼", resultStr);
                Thread thread = new Thread(Image_update);
                thread.start();
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Intent intent = new Intent(MainActivity2.this, MainActivity.class);
                intent.putExtra("result", resultStr);
                Log.d("result", resultStr);
                setResult(1, intent);
                finish();
            }
        } else {
            Log.d("Status ", "未分辨車牌號碼,請重拍");
        }
    }
}

public void copyDataBase() throws IOException {
    //  Common common = new Common();
    //  取得SK卡路徑/lpr.key
    String dst = Environment.getExternalStorageDirectory().toString() + "/lpr.key";

    File file = new File(dst);
    if (!file.exists()) {
        // file.createNewFile();
    } else {
        file.delete();
    }
    Log.d("File Name", file.toString());
    try {
        InputStream myInput = getAssets().open("lpr.key");
        OutputStream myOutput = new FileOutputStream(dst);
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.flush();
        myOutput.close();
        myInput.close();
    } catch (Exception e) {
        System.out.println("lpr.key" + "is not found");
    }
}

private Runnable Image_update = new Runnable() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("https://0d9dccd7eac8.ngrok.io/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    MyAPIService myAPIService = retrofit.create(MyAPIService.class);

    @Override
    public void run() {
        Log.d("Status ", "run");
        Log.d("resultStr ", resultStr);
        String url = "D:\Images\license_plate\";
        String imgStr = bitmap2base64(toBitmap(image));
        LicensePlate licensePlate = new LicensePlate();
        licensePlate.setsPlate(resultStr);
        licensePlate.setsPicPosition(url + resultStr);
        licensePlate.setImgStr(imgStr);

        Call<LicensePlate> call = myAPIService.uploadLicensePlate(licensePlate);
        call.enqueue(new Callback<LicensePlate>() {
            @Override
            public void onResponse(Call<LicensePlate> call, Response<LicensePlate> response) {
                if(response.isSuccessful()){
                    Log.d("Status ", "照片上傳成功");
                }else{
                    Log.d("Status ", "照片上傳失敗");
                    Log.d("response code ", response.code() + "");
                }
            }

            @Override
            public void onFailure(Call<LicensePlate> call, Throwable t) {
                Log.d("Status ", "onFailure");
                Log.d("Message ", t.getMessage());
            }
        });
    }
};

private String bitmap2base64(Bitmap bitmap){
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
    return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT).trim().replaceAll("\n", "").replaceAll("\r", "");
}

private Bitmap toBitmap(Image image) {
    Image.Plane[] planes = image.getPlanes();
    ByteBuffer yBuffer = planes[0].getBuffer();
    ByteBuffer uBuffer = planes[1].getBuffer();
    ByteBuffer vBuffer = planes[2].getBuffer();

    int ySize = yBuffer.remaining();
    int uSize = uBuffer.remaining();
    int vSize = vBuffer.remaining();

    byte[] nv21 = new byte[ySize + uSize + vSize];
    //U and V are swapped
    yBuffer.get(nv21, 0, ySize);
    vBuffer.get(nv21, ySize, vSize);
    uBuffer.get(nv21, ySize + vSize, uSize);

    YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21, image.getWidth(), image.getHeight(), null);
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    yuvImage.compressToJpeg(new Rect(0, 0, yuvImage.getWidth(), yuvImage.getHeight()), 75, out);

    byte[] imageBytes = out.toByteArray();
    return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
}

}

相机

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题
    DisplayMetrics metric = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metric);
    int metricwidth = metric.widthPixels; // 屏幕宽度(像素)
    int metricheight = metric.heightPixels; // 屏幕高度(像素)
    try {
        copyDataBase();
    } catch (IOException e) {
        e.printStackTrace();
    }
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);// 竖屏
    Configuration cf= this.getResources().getConfiguration(); //获取设置的配置信息
    int noriention=cf.orientation;
    requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏
    // // 屏幕常亮
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    setContentView(R.layout.activity_lpr2);
    findView();

}

private void findView() {
    surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
    re_c = (RelativeLayout) findViewById(R.id.re_c);

    DisplayMetrics metric = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metric);
    width = metric.widthPixels; // 屏幕宽度(像素)
    height = metric.heightPixels; // 屏幕高度(像素)

    if(myView==null)
    {
        if (isFatty)
        {
            myView = new LPRfinderView2(LPR2Activity.this, width, height, isFatty);
        }
        else
        {
            myView = new LPRfinderView2(LPR2Activity.this, width, height);
        }
        re_c.addView(myView);
    }



    surfaceHolder = surfaceView.getHolder();
    surfaceHolder.addCallback(LPR2Activity.this);
    surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    surfaceView.setFocusable(true);
    //surfaceView.invali.date();
}

public void copyDataBase() throws IOException {
    //  Common common = new Common();
    //  取得SK卡路徑/lpr.key
    String dst = Environment.getExternalStorageDirectory().toString() + "/lpr.key";

    File file = new File(dst);
    if (!file.exists()) {
        // file.createNewFile();
    } else {
        file.delete();
    }
    Log.d("File Name", file.toString());
    try {
        InputStream myInput = getAssets().open("lpr.key");
        OutputStream myOutput = new FileOutputStream(dst);
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.flush();
        myOutput.close();
        myInput.close();
    } catch (Exception e) {
        System.out.println("lpr.key" + "is not found");
    }
}

public void surfaceCreated(SurfaceHolder holder) {

    if (mycamera == null) {
        try {
            mycamera = Camera.open();
        } catch (Exception e) {
            e.printStackTrace();
            String mess = "打开摄像头失败";
            Toast.makeText(getApplicationContext(), mess, Toast.LENGTH_LONG).show();
            return;
        }
    }
    if(mycamera!=null)
    {
        try {

            mycamera.setPreviewDisplay(holder);
            timer2 = new Timer();
            if (timer == null)
            {
                timer = new TimerTask()
                {
                    public void run()
                    {
                        if (mycamera != null)
                        {
                            try
                            {
                                mycamera.autoFocus(new AutoFocusCallback()
                                {
                                    public void onAutoFocus(boolean success, Camera camera)
                                    {

                                    }
                                });
                            }
                            catch (Exception e)
                            {
                                e.printStackTrace();
                            }
                        }
                    };
                };
            }
            timer2.schedule(timer, 500, 2500);
            initCamera();
            //mycamera.startPreview();
            //mycamera.autoFocus(null);

        } catch (IOException e) {
            e.printStackTrace();

        }
    }
    if(api==null)
    {
        api= new LPR();
        String FilePath =Environment.getExternalStorageDirectory().toString()+"/lpr.key";
        int nRet = api.Init(this,m_ROI[0], m_ROI[1], m_ROI[2], m_ROI[3], preHeight, preWidth,FilePath);
        if(nRet!=0)
        {
            Toast.makeText(getApplicationContext(), "啟動失敗,請調整時間", Toast.LENGTH_SHORT).show();
            Log.d("nRet ", nRet + "");
            bInitKernal =false;
        }
        else
        {
            bInitKernal=true;
        }
    }
    if(alertDialog==null){
        alertDialog = new AlertDialog.Builder(this).create();
        alertDialoginfo = new AlertDialog.Builder(this).create();
    }

}

@Override
public void surfaceChanged(final SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    try {
        if (mycamera != null) {
            mycamera.setPreviewCallback(null);
            mycamera.stopPreview();
            mycamera.release();
            mycamera = null;
        }
    } catch (Exception e) {
    }
    if(bInitKernal){
        bInitKernal=false;
        api = null;
    }
    if(toast!=null){
        toast.cancel();
        toast = null;
    }
    if(timer2!=null){
        timer2.cancel();
        timer2=null;
    }
    if(alertDialog!=null)
    {
        alertDialog.dismiss();
        alertDialog.cancel();
        alertDialog=null;
    }
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        try {
            if (mycamera != null) {
                mycamera.setPreviewCallback(null);
                mycamera.stopPreview();
                mycamera.release();
                mycamera = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(bInitKernal)
        {
            bInitKernal=false;
            api = null;
        }
        finish();
        if(toast!=null){
            toast.cancel();
            toast=null;
        }
        if(timer2!=null){
            timer2.cancel();
            timer2=null;
        }
        if(alertDialog!=null)
        {
            alertDialog.cancel();
            alertDialog=null;
        }
    }
    return super.onKeyDown(keyCode, event);
}

@TargetApi(14)
private void initCamera() {
    Camera.Parameters parameters = mycamera.getParameters();
    List<Camera.Size> list = parameters.getSupportedPreviewSizes();
    preWidth = list.get(4).width;
    preHeight = list.get(4).height;
    parameters.setPictureFormat(PixelFormat.JPEG);

    parameters.setPreviewSize(preWidth,preHeight);
    if (!bROI) {
        int l,t,r,b;
        l =  100;
        r = width-100;
        int ntmpH =(r-l)*58/100;
        t = (height-ntmpH)/2;
        b =  t+ntmpH;
        double proportion = (double) width / (double) preHeight;
        double hproportion=(double)height/(double)  preWidth;
        l = (int) (l /proportion);
        t = (int) (t /hproportion);
        r = (int) (r /proportion);
        b = (int) (b / hproportion);
        m_ROI[0]=l;
        m_ROI[1]=t;
        m_ROI[2]=r;
        m_ROI[3]=b;
        bROI = true;
    }
    if (parameters.getSupportedFocusModes().contains(
            parameters.FOCUS_MODE_AUTO))
    {
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);// 1连续对焦
    }

    mycamera.setPreviewCallback(LPR2Activity.this);
    mycamera.setParameters(parameters);
    mycamera.setDisplayOrientation(90); //一般機種
    mycamera.startPreview();
}
public void onPreviewFrame(byte[] data, Camera camera) {

    tackData = data;
    Log.d("data length ", data.length + "");
    ByteArrayInputStream bis = new ByteArrayInputStream(data);
    resultStr = "";
    if (!leaving&& bInitKernal ) {
        Log.d("Status ", "開始判斷");
        byte result[];//[] = new byte[10];
        String res="";
        result = api.VideoRec(tackData, preWidth, preHeight, 1);
        Log.d("preWidth ", preWidth + "");
        Log.d("preHeight", preHeight + "");
        Log.d("width ", width + "");
        Log.d("height", height + "");
        try {
            res = new String(result,"gb2312");
            Log.d("try ", res);
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            Log.d("Exception ", e.getMessage());
            e.printStackTrace();
        }
        Log.d("res ", res);
        if(res!=null&&!"".equals(res.trim()))
        {
            Camera.Parameters parameters = mycamera.getParameters();
            resultStr =res.trim();
            if (resultStr != "") {
                leaving = true;

                //拍照音效
                MediaActionSound sound = null;
                if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
                    sound = new MediaActionSound();
                    sound.play(MediaActionSound.SHUTTER_CLICK);
                }

                Intent intent = new Intent();
                intent.putExtra("strPltNo",resultStr);
                setResult(2,intent);
                finish();
            }else{
                Log.d("Status ", "未分配車牌號碼,請重拍");
            }
        }
    }
}

@RequiresApi(api = Build.VERSION_CODES.CUPCAKE)
public String pictureName() {
    String str = "";
    Time t = new Time();
    t.setToNow(); // 取得系统时间。
    int year = t.year;
    int month = t.month + 1;
    int date = t.monthDay;
    int hour = t.hour; // 0-23
    int minute = t.minute;
    int second = t.second;
    if (month < 10)
        str = String.valueOf(year) + "0" + String.valueOf(month);
    else {
        str = String.valueOf(year) + String.valueOf(month);
    }
    if (date < 10)
        str = str + "0" + String.valueOf(date + "_");
    else {
        str = str + String.valueOf(date + "_");
    }
    if (hour < 10)
        str = str + "0" + String.valueOf(hour);
    else {
        str = str + String.valueOf(hour);
    }
    if (minute < 10)
        str = str + "0" + String.valueOf(minute);
    else {
        str = str + String.valueOf(minute);
    }
    if (second < 10)
        str = str + "0" + String.valueOf(second);
    else {
        str = str + String.valueOf(second);
    }
    return str;
}

public void Leave(View view) {
    Intent intent = new Intent();
    intent.putExtra("strPltNo","");
    setResult(3,intent);
    finish();
}

}

CameraX Log

Camera Log

关于图像分析分辨率,ImageAnalysis.Builder.setTargetResolution() 的文档指出:

The maximum available resolution that could be selected for an ImageAnalysis is limited to be under 1080p.

因此,将尺寸设置为 2560x800 不会如您所愿。在 return 中,CameraX 似乎正在选择与您请求的纵横比相同的最大 ImageAnalysis 分辨率 (2560/800 = 1280/400)。