YUV_420_888 interprétation sur Samsung Galaxy S7 (Camera2)

J'ai écrit une conversion de YUV_420_888 vers Bitmap, compte tenu de la logique suivante (si je comprends bien):

Entrez la description de l'image ici

  • Comment envelopper le texte à la ligne suivante dans un Android TextView?
  • FindViewById () renvoie null lorsque je l'appelle sur onCreate ()
  • ConsumerProguardFiles vs ProguardFiles
  • Android / Java: Appel d'une méthode utilisant la réflexion?
  • Bloquer les SMS entrants sur Android 4 ou supérieur?
  • Android: this.getApplication () renvoie le pointeur NULL
  • Pour résumer l'approche: les coordonnées du noyau x et y sont congruentes à la fois avec les x et y de la partie non-rembourrée du Y-Plane (allocation 2d) et les x et y de la sortie-Bitmap. Les U- et V-Planes, cependant, ont une structure différente de celle du Y-Plane, car ils utilisent 1 octet pour une couverture de 4 pixels et, en plus, peuvent avoir un PixelStride qui est plus d'un, en plus ils pourraient Ont également un rembourrage différent de celui du Y-Plane. Par conséquent, pour accéder aux U et aux V efficacement par le noyau, je les ai mis dans les allocations 1-d et créé un index "uvIndex" qui donne la position des U et V correspondants dans cette allocation 1-d, pour donné ( X, y) coordonnées dans le plan Y (non rembourré) (et, par conséquent, le bitmap de sortie).

    Afin de maintenir le rs-Kernel maigre, j'ai exclu la zone de rembourrage dans le yPlane en coiffant la gamme x via LaunchOptions (cela reflète le RowStride du plan y qui peut donc être ignoré DANS LE Nœud). Nous devons donc considérer le uvPixelStride et uvRowStride dans uvIndex, c'est-à-dire l'index utilisé pour accéder aux valeurs u et v.

    C'est mon code:

    Renderscript Kernel, nommé yuv420888.rs

    #pragma version(1) #pragma rs java_package_name(com.xxxyyy.testcamera2); #pragma rs_fp_relaxed int32_t width; int32_t height; uint picWidth, uvPixelStride, uvRowStride ; rs_allocation ypsIn,uIn,vIn; // The LaunchOptions ensure that the Kernel does not enter the padding zone of Y, so yRowStride can be ignored WITHIN the Kernel. uchar4 __attribute__((kernel)) doConvert(uint32_t x, uint32_t y) { // index for accessing the uIn's and vIn's uint uvIndex= uvPixelStride * (x/2) + uvRowStride*(y/2); // get the y,u,v values uchar yps= rsGetElementAt_uchar(ypsIn, x, y); uchar u= rsGetElementAt_uchar(uIn, uvIndex); uchar v= rsGetElementAt_uchar(vIn, uvIndex); // calc argb int4 argb; argb.r = yps + v * 1436 / 1024 - 179; argb.g = yps -u * 46549 / 131072 + 44 -v * 93604 / 131072 + 91; argb.b = yps +u * 1814 / 1024 - 227; argb.a = 255; uchar4 out = convert_uchar4(clamp(argb, 0, 255)); return out; } 

    Côté Java:

      private Bitmap YUV_420_888_toRGB(Image image, int width, int height){ // Get the three image planes Image.Plane[] planes = image.getPlanes(); ByteBuffer buffer = planes[0].getBuffer(); byte[] y = new byte[buffer.remaining()]; buffer.get(y); buffer = planes[1].getBuffer(); byte[] u = new byte[buffer.remaining()]; buffer.get(u); buffer = planes[2].getBuffer(); byte[] v = new byte[buffer.remaining()]; buffer.get(v); // get the relevant RowStrides and PixelStrides // (we know from documentation that PixelStride is 1 for y) int yRowStride= planes[0].getRowStride(); int uvRowStride= planes[1].getRowStride(); // we know from documentation that RowStride is the same for u and v. int uvPixelStride= planes[1].getPixelStride(); // we know from documentation that PixelStride is the same for u and v. // rs creation just for demo. Create rs just once in onCreate and use it again. RenderScript rs = RenderScript.create(this); //RenderScript rs = MainActivity.rs; ScriptC_yuv420888 mYuv420=new ScriptC_yuv420888 (rs); // Y,U,V are defined as global allocations, the out-Allocation is the Bitmap. // Note also that uAlloc and vAlloc are 1-dimensional while yAlloc is 2-dimensional. Type.Builder typeUcharY = new Type.Builder(rs, Element.U8(rs)); typeUcharY.setX(yRowStride).setY(height); Allocation yAlloc = Allocation.createTyped(rs, typeUcharY.create()); yAlloc.copyFrom(y); mYuv420.set_ypsIn(yAlloc); Type.Builder typeUcharUV = new Type.Builder(rs, Element.U8(rs)); // note that the size of the u's and v's are as follows: // ( (width/2)*PixelStride + padding ) * (height/2) // = (RowStride ) * (height/2) // but I noted that on the S7 it is 1 less... typeUcharUV.setX(u.length); Allocation uAlloc = Allocation.createTyped(rs, typeUcharUV.create()); uAlloc.copyFrom(u); mYuv420.set_uIn(uAlloc); Allocation vAlloc = Allocation.createTyped(rs, typeUcharUV.create()); vAlloc.copyFrom(v); mYuv420.set_vIn(vAlloc); // handover parameters mYuv420.set_picWidth(width); mYuv420.set_uvRowStride (uvRowStride); mYuv420.set_uvPixelStride (uvPixelStride); Bitmap outBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Allocation outAlloc = Allocation.createFromBitmap(rs, outBitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); Script.LaunchOptions lo = new Script.LaunchOptions(); lo.setX(0, width); // by this we ignore the y's padding zone, ie the right side of x between width and yRowStride lo.setY(0, height); mYuv420.forEach_doConvert(outAlloc,lo); outAlloc.copyTo(outBitmap); return outBitmap; } 

    Test sur Nexus 7 (API 22), cela permet de générer de bons bits de couleur. Cependant, cet appareil a des tours de pixels triviaux (= 1) et aucun rembourrage (c.-à-d. Rowstride = largeur). Test sur le tout nouveau Samsung S7 (API 23) Je reçois des images dont les couleurs ne sont pas correctes – sauf celles des vertes. Mais l'image ne montre pas un biais général vers le vert, il semble que les couleurs non vertes ne soient pas reproduites correctement. Notez que le S7 applique un mode de navigation en mode veille de 2, et aucun rembourrage.

    Étant donné que la ligne de code la plus cruciale se trouve dans le code rs, l'accès aux plans u / v uint uvIndex = (…) je pense qu'il pourrait y avoir le problème, probablement avec un examen incorrect des cadences de pixels ici. Est-ce que quelqu'un voit la solution? Merci.

    MISE À JOUR: J'ai tout vérifié, et je suis bien sûr que le code concernant l'accès de y, u, v est correct. Le problème doit donc être avec les valeurs u et v elles-mêmes. Les couleurs non vert ont une inclinaison violette, et en regardant les valeurs u, v elles semblent être dans une gamme assez étroite d'environ 110-150. Est-il vraiment possible que nous devons faire face aux conversions YUV -> RBG spécifiques à un périphérique …?! Ai-je manqué quelque chose?

    MISE À JOUR 2: a corrigé le code, il fonctionne maintenant, grâce à Feedback de Eddy.

  • Notifications en temps spécifique tous les jours, Android
  • Meteor recharge sans fin après le redéploiement sur le serveur avec appcache
  • Le widget Android fonctionne bien dans l'émulateur mais sur le téléphone, il devient le widget de l'application Google
  • Comment déterminer si un raccourci de l'écran d'accueil existe?
  • Existe-t-il un moyen de modifier le nom d'une application sur l'application Google Play?
  • GetLayoutInflater () dans le fragment
  • 5 Solutions collect form web for “YUV_420_888 interprétation sur Samsung Galaxy S7 (Camera2)”

    Regarder

     floor((float) uvPixelStride*(x)/2) 

    Qui calcule votre décalage de ligne U, V (uv_row_offset) à partir de la coordonnée Y x.

    Si uvPixelStride = 2, alors que x augmente:

     x = 0, uv_row_offset = 0 x = 1, uv_row_offset = 1 x = 2, uv_row_offset = 2 x = 3, uv_row_offset = 3 

    Et c'est incorrect. Il n'y a pas de valeur de pixel U / V valide à uv_row_offset = 1 ou 3, depuis uvPixelStride = 2.

    Tu veux

     uvPixelStride * floor(x/2) 

    (En supposant que vous ne vous fiez pas à vous-même pour vous rappeler le comportement critique de la fracture entière, si vous le faites alors):

     uvPixelStride * (x/2) 

    Cela devrait suffire

    Avec cela, votre cartographie devient:

     x = 0, uv_row_offset = 0 x = 1, uv_row_offset = 0 x = 2, uv_row_offset = 2 x = 3, uv_row_offset = 2 

    Voyez si cela corrige les erreurs de couleur. Dans la pratique, l'adresse incorrecte ici signifierait que tous les autres échantillons de couleurs seraient du mauvais plan de couleur, car il est probable que les données YUV sous-jacentes sont semi-planétaires (de sorte que le plan U commence au plan V + 1 octet, les deux plans intercalés)

    Pour les personnes qui rencontrent une erreur

     android.support.v8.renderscript.RSIllegalArgumentException: Array too small for allocation type 

    Utilisez buffer.capacity() au lieu de buffer.remaining()

    Et si vous avez déjà effectué des opérations sur l'image, vous devrez appeler la méthode de rewind() sur le tampon.

    Sur un Samsung Galaxy Tab 5 (Tablette), la version Android 5.1.1 (22), avec le prétendu format YUV_420_888, les calculs renderscript suivants fonctionnent bien et produisent des couleurs correctes:

     uchar yValue = rsGetElementAt_uchar(gCurrentFrame, x + y * yRowStride); uchar vValue = rsGetElementAt_uchar(gCurrentFrame, ( (x/2) + (y/4) * yRowStride ) + (xSize * ySize) ); uchar uValue = rsGetElementAt_uchar(gCurrentFrame, ( (x/2) + (y/4) * yRowStride ) + (xSize * ySize) + (xSize * ySize) / 4); 

    Je ne comprends pas pourquoi la valeur horizontale (c'est-à-dire y) est mise à l'échelle d'un facteur de quatre au lieu de deux, mais ça marche bien. J'ai également besoin d'éviter l'utilisation de rsGetElementAtYuv_uchar_Y | U | V. Je crois que la valeur de l'équation d'allocation associée est définie à zéro à la place de quelque chose de propre. L'utilisation de rsGetElementAt_uchar () est un travail raisonnable.

    Sur un Samsung Galaxy S5 (Smart Phone), une version Android 5.0 (21), avec un prétendu format YUV_420_888, je ne peux pas récupérer les valeurs u et v, elles apparaissent comme tous les zéros. Il en résulte une image verte. Luminous est correct, mais l'image est retournée verticalement.

    Ce code nécessite l'utilisation de la bibliothèque de compatibilité RenderScript (android.support.v8.renderscript. *).

    Afin d'obtenir la bibliothèque de compatibilité pour fonctionner avec l'API Android 23, j'ai été mis à jour pour gradle-plugin 2.1.0 et Build-Tools 23.0.3 selon la réponse de Miao Wang à Comment créer des scripts Renderscript sur Android Studio et les faire fonctionner?

    Si vous suivez sa réponse et obtenez une erreur "Gradle version 2.10 est nécessaire" apparaît, ne PAS changer

     classpath 'com.android.tools.build:gradle:2.1.0' 

    Au lieu de cela, mettez à jour le champ distributionUrl du fichier Project \ gradle \ wrapper \ gradle-wrapper.properties vers

     distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 

    Et modifiez Fichier> Paramètres> Constructions, Exécution, Déploiement> Outils de génération> Gradle> Gradle pour utiliser le wrapper gradle par défaut "Gradle Version 2.10". Erreur .

    En outre, pour quelqu'un d'autre

    Android.support.v8.renderscript.RSIllegalArgumentException: Array trop petit pour le type d'allocation

    Je l'ai corrigé en changeant yAlloc.copyFrom (y); À yAlloc.copy1DRangeFrom (0, y.length, y);

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.