Converting preview frame to bitmap

I know the subject was on the board many times, but i can not get it work anyhow... I want to save view frames from preview to jpeg files. It looks more or less(code is simplified- without additional logic, exception etc) like this...

public void onPreviewFrame(byte[] data, Camera camera) {
  int width = camera.getParameters().getPreviewSize().width;
  int height = camera.getParameters().getPreviewSize().height;


  final int[] rgb = decodeYUV420SP(data, width, height);

  Bitmap bmp = Bitmap.createBitmap(rgb, width, height,Bitmap.Config.ARGB_8888);

  String filename="/sdcard/file" + (index++)+ ".jpg"; 
  FileOutputStream out;
  out = new FileOutputStream(filename);
  bmp.compress(Bitmap.CompressFormat.JPEG, 90, out);
  out.flush();
  out.close();
  out=null;

 }

Here is the one of the methods i tried to convert colors(from this board i believe)

public int[] decodeYUV420SP( byte[] yuv420sp, int width, int height) {   

    final int frameSize = width * height;   

    int rgb[]=new int[width*height];   
    for (int j = 0, yp = 0; j < height; j++) {   
        int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;   
        for (int i = 0; i < width; i++, yp++) {   
            int y = (0xff & ((int) yuv420sp[yp])) - 16;   
            if (y < 0) y = 0;   
            if ((i & 1) == 0) {   
                v = (0xff & yuv420sp[uvp++]) - 128;   
                u = (0xff & yuv420sp[uvp++]) - 128;   
            }   

            int y1192 = 1192 * y;   
            int r = (y1192 + 1634 * v);   
            int g = (y1192 - 833 * v - 400 * u);   
            int b = (y1192 + 2066 * u);   

            if (r < 0) r = 0; else if (r > 262143) r = 262143;   
            if (g < 0) g = 0; else if (g > 262143) g = 262143;   
            if (b < 0) b = 0; else if (b > 262143) b = 262143;   

            rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) &    
    0xff00) | ((b >> 10) & 0xff);   


        }   
    }   
    return rgb;   
    } 

The problem is that the picture always looks like three 'strange green pictures'... I ama new user so i can't post it:(

I dont know if it has something to do with the size or what but i am stuck... Can you support me with it?


Alternatively, if you DO need a bitmap for some reason, and/or want to do this without creating a YUVImage and compressing to JPEG, you can use the handy RenderScript 'ScriptIntrinsicYuvToRGB' (API 17+):

@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 
   Bitmap bitmap = Bitmap.createBitmap(r.width(), r.height(), Bitmap.Config.ARGB_8888);
    Allocation bmData = renderScriptNV21ToRGBA8888(
        mContext, r.width(), r.height(), data);
    bmData.copyTo(bitmap);
}

public Allocation renderScriptNV21ToRGBA8888(Context context, int width, int height, byte[] nv21) {
  RenderScript rs = RenderScript.create(context);
  ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));

  Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(nv21.length);
  Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);

  Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
  Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);

  in.copyFrom(nv21);

  yuvToRgbIntrinsic.setInput(in);
  yuvToRgbIntrinsic.forEach(out);
  return out;
}

Simply saving to a jpeg is an easier task than converting to bitmap, no need for that YUV decoding code thanks to YuvImage class.

import android.graphics.YuvImage; 

@Override 
public void onPreviewFrame(byte[] data, Camera camera) { 
    try { 
        Camera.Parameters parameters = camera.getParameters(); 
        Size size = parameters.getPreviewSize(); 
        YuvImage image = new YuvImage(data, parameters.getPreviewFormat(), 
                size.width, size.height, null); 
        File file = new File(Environment.getExternalStorageDirectory(), "out.jpg"); 
        FileOutputStream filecon = new FileOutputStream(file); 
        image.compressToJpeg( 
                new Rect(0, 0, image.getWidth(), image.getHeight()), 90, 
                filecon); 
    } catch (FileNotFoundException e) { 
        Toast toast = Toast 
                .makeText(getBaseContext(), e.getMessage(), 1000); 
        toast.show(); 
    } 
} 

If I am not mistaken, you want the width and height of the picture to decode, not the width and heigth of the preview.


In order to avoid degraded JPEG image on the output (e.g. with interleaving green/red spots), you need to flush and close FileOutputStream

FileOutputStream filecon = new FileOutputStream(file); 
image.compressToJpeg( 
            new Rect(0, 0, image.getWidth(), image.getHeight()), 90, 
        filecon);
filecon.flush();
filecon.close();