Android上的相机预览处理

Posted

技术标签:

【中文标题】Android上的相机预览处理【英文标题】:Camera preview processing on Android 【发布时间】:2012-03-20 18:38:13 【问题描述】:

我正在为我的机器人在 android 上制作一个线跟随器(学习 Java/Android 编程),目前我正面临图像处理问题:相机预览返回一个名为 YUV 的图像格式,我想将其转换为阈值以知道线在哪里,如何做到这一点?

到目前为止,我已经成功获得一些东西,也就是说,我绝对可以从相机预览中读取数据,并且奇迹般地甚至可以知道光强度是否超过或低于某个值屏幕上的特定区域。我的目标是在相机预览的叠加层上绘制机器人的路径,这在某种程度上也有效,但问题在于 YUV 管理。

正如你所见,不仅黑暗区域被横向绘制,而且它还会重复 4 次并且预览图像被拉伸,我不知道如何解决这些问题。

以下是代码的相关部分:

public void surfaceCreated(SurfaceHolder arg0) 
    // TODO Auto-generated method stub

    // camera setup
    mCamera = Camera.open();

    Camera.Parameters parameters = mCamera.getParameters();
    List<Camera.Size> sizes = parameters.getSupportedPreviewSizes();
    for(int i=0; i<sizes.size(); i++)
    
        Log.i("CS", i+" - width: "+sizes.get(i).width+" height: "+sizes.get(i).height+" size: "+(sizes.get(i).width*sizes.get(i).height));
    

    // change preview size
    final Camera.Size cs = sizes.get(8);
    parameters.setPreviewSize(cs.width, cs.height);

    // initialize image data array
    imgData = new int[cs.width*cs.height];

    // make picture gray scale
    parameters.setColorEffect(Camera.Parameters.EFFECT_MONO);
    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
    mCamera.setParameters(parameters);

    // change display size
    LayoutParams params = (LayoutParams) mSurfaceView.getLayoutParams();
    params.height = (int) (mSurfaceView.getWidth()*cs.height/cs.width);
    mSurfaceView.setLayoutParams(params);

    LayoutParams overlayParams = (LayoutParams) swOverlay.getLayoutParams();
    overlayParams.width = mSurfaceView.getWidth();
    overlayParams.height = mSurfaceView.getHeight();
    swOverlay.setLayoutParams(overlayParams);

    try
    
        mCamera.setPreviewDisplay(mSurfaceHolder);
        mCamera.setDisplayOrientation(90);
        mCamera.startPreview();
    
    catch (IOException e)
    
        e.printStackTrace();
        mCamera.stopPreview();
        mCamera.release();
    

    // callback every time a new frame is available
    mCamera.setPreviewCallback(new PreviewCallback() 
        public void onPreviewFrame(byte[] data, Camera camera)
        
            // create bitmap from camera preview
            int pixel, pixVal, frameSize = cs.width*cs.height;
            for(int i=0; i<frameSize; i++)
            
                pixel = (0xff & ((int) data[i])) - 16;
                if(pixel < threshold)
                
                    pixVal = 0;
                
                else
                
                    pixVal = 1;
                
                imgData[i] = pixVal;
            

            int cp = imgData[(int) (cs.width*(0.5+(cs.height/2)))];

            //Log.i("CAMERA", "Center pixel RGB: "+cp);
            debug.setText("Center pixel: "+cp);

            // process preview image data
            Paint paint = new Paint();
            paint.setColor(Color.YELLOW);


            int start, finish, last;
            start = finish = last = -1;
            float x_ratio = mSurfaceView.getWidth()/cs.width;
            float y_ratio = mSurfaceView.getHeight()/cs.height;

            // display calculated path on overlay using canvas
            Canvas overlayCanvas = overlayHolder.lockCanvas();
            overlayCanvas.drawColor(0, Mode.CLEAR);

            // start by finding the tape from bottom of the screen      
            for(int y=cs.height; y>0; y--)
            
                for(int x=0; x<cs.width; x++)
                
                    pixel = imgData[y*cs.height+x];

                    if(pixel == 1 && last == 0 && start == -1)
                    
                        start = x;
                    
                    else if(pixel == 0 && last == 1 && finish == -1)
                    
                        finish = x;
                        break;
                    
                    last = pixel;
                
                //overlayCanvas.drawLine(start*x_ratio, y*y_ratio, finish*x_ratio, y*y_ratio, paint);
                //start = finish = last = -1;
            
            overlayHolder.unlockCanvasAndPost(overlayCanvas);
        
    );

由于在release 之后调用了某些方法,此代码有时会在退出应用程序时产生错误,这是我的问题中最少的。


更新:

现在方向问题已经解决了(CCD传感器方向)我仍然面临重复问题,这可能与我的YUV数据管理有关......

【问题讨论】:

【参考方案1】:

您的表面和相机管理看起来正确,但我会仔细检查相机实际上接受了预览大小设置(某些相机实现会默默地拒绝某些设置)

当您使用纵向模式时,您必须记住,相机不会对 prhone 方向放屁 - 它的坐标原点由 CCD 芯片确定,并且始终位于右上角,扫描方向是从上到下,并且从右到左 - 与您的叠加画布完全不同。 (但如果你在横向模式下,一切都是正确的;)) - 这肯定是奇怪的绘图结果的来源

您的 threshloding 有点幼稚,在现实生活中不是很有用 - 我建议自适应 threshloding。在我们的 javaocr 项目(纯 java,也有 android 演示)中,我们实现了高效的 sauvola 二值化(参见演示):

http://sourceforge.net/projects/javaocr/

性能二值化可以改进为仅适用于单个图像行(欢迎使用补丁)

图像的 UV 部分很容易出现问题 - 默认格式为 NV21,亮度优先 这只是字节流,你根本不需要图像的 UV 部分(查看上面的演示)

【讨论】:

谢谢你的CCD提示,确实是问题,现在重复怎么办,这肯定与我的YUV管理有关。一旦我让它工作,我将使用动态阈值对其进行优化。 ...comendted about liminance 问题

以上是关于Android上的相机预览处理的主要内容,如果未能解决你的问题,请参考以下文章

三星 Galaxy S3 上的 Android 相机预览不正确

4:3 平板电脑上的 Android 相机预览纵横比

使用 Android L 和 Camera2 API 处理相机预览图像数据

Android:编码的相机预览帧总是横向的。如何旋转?

Android:如何通过回调显示相机预览?

Android Camera API/Camera2 API 相机预览及滤镜贴纸等处理