2017-05-17 2 views
1

J'ai utilisé la classe ImageUtil prévu dans https://stackoverflow.com/a/40152147/2949966 dans mon git: https://github.com/ahasbini/cameraview/tree/camera_preview_imp (note la mise en œuvre est en camera_preview_imp branche) pour mettre en œuvre un rappel aperçu du cadre. Un ImageReader est configuré pour prévisualiser les images au format ImageFormat.YUV_420_888 qui sera converti en ImageFormat.JPEG en utilisant la classe ImageUtil et l'envoyer au rappel de trame. L'application de démonstration enregistre une image depuis le rappel vers un fichier toutes les 50 images. Toutes les images de trame enregistrées sont coming out déformée similaire à ci-dessous:Conversion YUV_420_888 au format JPEG et l'enregistrement des résultats des fichiers d'image déformée

enter image description here

Si j'ai changé le ImageReader à utiliser ImageFormat.JPEG à la place en faisant les changements suivants dans Camera2:

mPreviewImageReader = ImageReader.newInstance(previewSize.getWidth(), 
    previewSize.getHeight(), ImageFormat.JPEG, /* maxImages */ 2); 
mCamera.createCaptureSession(Arrays.asList(surface, mPreviewImageReader.getSurface()), 
    mSessionCallback, null); 

la l'image arrive correctement sans aucune distorsion mais la fréquence d'images chute de manière significative et la vue commence à être décalée. Par conséquent, je crois que la classe ImageUtil ne convertit pas correctement.

+0

'l'image finale avec distorsion est l'image écrite dans un fichier? –

+0

Ma mauvaise, question éditée pour effacer la confusion. – ahasbini

+0

où je peux voir la méthode 'onImageAvailable (ImageReader reader)' (ImageReader.OnImageAvailableListener)? –

Répondre

5

solution fournie par @ Volodymyr-Kulyk ne prend pas en considération la foulée de la ligne des plans dans l'image. Ci-dessous le code fait le tour (image est de type android.media.Image):

data = NV21toJPEG(YUV420toNV21(image), image.getWidth(), image.getHeight(), 100); 

Et les mises en œuvre:

private static byte[] NV21toJPEG(byte[] nv21, int width, int height, int quality) { 
    ByteArrayOutputStream out = new ByteArrayOutputStream(); 
    YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null); 
    yuv.compressToJpeg(new Rect(0, 0, width, height), quality, out); 
    return out.toByteArray(); 
} 

private static byte[] YUV420toNV21(Image image) { 
    Rect crop = image.getCropRect(); 
    int format = image.getFormat(); 
    int width = crop.width(); 
    int height = crop.height(); 
    Image.Plane[] planes = image.getPlanes(); 
    byte[] data = new byte[width * height * ImageFormat.getBitsPerPixel(format)/8]; 
    byte[] rowData = new byte[planes[0].getRowStride()]; 

    int channelOffset = 0; 
    int outputStride = 1; 
    for (int i = 0; i < planes.length; i++) { 
     switch (i) { 
      case 0: 
       channelOffset = 0; 
       outputStride = 1; 
       break; 
      case 1: 
       channelOffset = width * height + 1; 
       outputStride = 2; 
       break; 
      case 2: 
       channelOffset = width * height; 
       outputStride = 2; 
       break; 
     } 

     ByteBuffer buffer = planes[i].getBuffer(); 
     int rowStride = planes[i].getRowStride(); 
     int pixelStride = planes[i].getPixelStride(); 

     int shift = (i == 0) ? 0 : 1; 
     int w = width >> shift; 
     int h = height >> shift; 
     buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift)); 
     for (int row = 0; row < h; row++) { 
      int length; 
      if (pixelStride == 1 && outputStride == 1) { 
       length = w; 
       buffer.get(data, channelOffset, length); 
       channelOffset += length; 
      } else { 
       length = (w - 1) * pixelStride + 1; 
       buffer.get(rowData, 0, length); 
       for (int col = 0; col < w; col++) { 
        data[channelOffset] = rowData[col * pixelStride]; 
        channelOffset += outputStride; 
       } 
      } 
      if (row < h - 1) { 
       buffer.position(buffer.position() + rowStride - length); 
      } 
     } 
    } 
    return data; 
} 

méthode a été obtenu à partir de la link suivante.

1

Mise à jour ImageUtil:

public final class ImageUtil { 

    public static byte[] NV21toJPEG(byte[] nv21, int width, int height, int quality) { 
     ByteArrayOutputStream out = new ByteArrayOutputStream(); 
     YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null); 
     yuv.compressToJpeg(new Rect(0, 0, width, height), quality, out); 
     return out.toByteArray(); 
    } 

    // nv12: true = NV12, false = NV21 
    public static byte[] YUV_420_888toNV(ByteBuffer yBuffer, ByteBuffer uBuffer, ByteBuffer vBuffer, boolean nv12) { 
     byte[] nv; 

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

     nv = new byte[ySize + uSize + vSize]; 

     yBuffer.get(nv, 0, ySize); 
     if (nv12) {//U and V are swapped 
      vBuffer.get(nv, ySize, vSize); 
      uBuffer.get(nv, ySize + vSize, uSize); 
     } else { 
      uBuffer.get(nv, ySize , uSize); 
      vBuffer.get(nv, ySize + uSize, vSize); 
     } 
     return nv; 
    } 

    public static byte[] YUV_420_888toI420SemiPlanar(ByteBuffer yBuffer, ByteBuffer uBuffer, ByteBuffer vBuffer, 
                int width, int height, boolean deInterleaveUV) { 
     byte[] data = YUV_420_888toNV(yBuffer, uBuffer, vBuffer, deInterleaveUV); 
     int size = width * height; 
     if (deInterleaveUV) { 
      byte[] buffer = new byte[3 * width * height/2]; 

      // De-interleave U and V 
      for (int i = 0; i < size/4; i += 1) { 
       buffer[i] = data[size + 2 * i + 1]; 
       buffer[size/4 + i] = data[size + 2 * i]; 
      } 
      System.arraycopy(buffer, 0, data, size, size/2); 
     } else { 
      for (int i = size; i < data.length; i += 2) { 
       byte b1 = data[i]; 
       data[i] = data[i + 1]; 
       data[i + 1] = b1; 
      } 
     } 
     return data; 
    } 
} 

des opérations d'écriture dans le fichier au format JPEG byte[] data:

//image.getPlanes()[0].getBuffer(), image.getPlanes()[1].getBuffer() 
//image.getPlanes()[2].getBuffer(), image.getWidth(), image.getHeight() 
byte[] nv21 = ImageUtil.YUV_420_888toI420SemiPlanar(yBuffer, uBuffer, vBuffer, width, height, false); 
byte[] data = ImageUtil.NV21toJPEG(nv21, width, height, 100); 
//now write `data` to file 

!!! n'oubliez pas de fermer l'image après le traitement !!!

image.close(); 
+0

mises à jour publiées dans le chat: http://chat.stackoverflow.com/rooms/144450/discussion-between-ahasbini-and-volodymyr-kulyk – ahasbini