YUV_420_888 Interpretation auf Samsung Galaxy S7 (Camera2)

Ich habe eine Konvertierung von YUV_420_888 nach Bitmap geschrieben, unter Berücksichtigung der folgenden Logik (so wie ich es verstehe):

Um den Ansatz zusammenzufassen: Die Koordinaten x und y des Kernels stimmen sowohl mit x und y des nicht aufgefüllten Teils der Y-Ebene (2D-Zuordnung) als auch mit x und y der Ausgabe-Bitmap überein. Die U- und V-Ebene haben jedoch eine andere Struktur als die Y-Ebene, da sie 1 Byte für die Abdeckung von 4 Pixeln verwenden und darüber hinaus möglicherweise eine PixelStride haben, die mehr als eine ist haben auch eine Polsterung, die sich von der der Y-Plane unterscheiden kann. Um vom Kernel effizient auf die U's und V's zugreifen zu können, habe ich sie in 1-d-Zuordnungen aufgeteilt und einen Index "uvIndex" erstellt, der die Position der entsprechenden U- und V-Werte innerhalb dieser 1-d-Zuordnung angibt ( x, y) -Koordinaten in der (nicht aufgefüllten) Y-Ebene (und damit in der ausgegebenen Bitmap).

Um den rs-Kernel schlank zu halten, habe ich den Auffüllbereich in der yPlane ausgeschlossen, indem ich den x-Bereich über LaunchOptions begrenzt habe (dies spiegelt die RowStride der y-Ebene wider, die somit INNERHALB des Kernels ignoriert werden kann). Wir müssen also nur uvPixelStride und uvRowStride im uvIndex berücksichtigen, d. H. Den Index, der für den Zugriff auf die u- und v-Werte verwendet wird.

Dies ist mein Code:

Renderscript Kernel mit dem Namen 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;
}

Java Seite:

    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, i.e. the right side of x between width and yRowStride
    lo.setY(0, height);

    mYuv420.forEach_doConvert(outAlloc,lo);
    outAlloc.copyTo(outBitmap);

    return outBitmap;
}

Testing auf Nexus 7 (API 22) gibt dies schöne Farb-Bitmaps zurück. Dieses Gerät hat jedoch triviale Pixelabstände (= 1) und kein Auffüllen (d. H. Zeilenabstand = Breite). Beim Testen mit dem brandneuen Samsung S7 (API 23) erhalte ich Bilder, deren Farben nicht korrekt sind - mit Ausnahme der grünen. Das Bild zeigt jedoch keine generelle Tendenz zu Grün, es scheint nur, dass nicht-grüne Farben nicht korrekt wiedergegeben werden. Beachten Sie, dass die S7 einen u / v-Pixelabstand von 2 und kein Auffüllen anwendet.

Da die wichtigste Codezeile innerhalb des rs-Codes der Zugriff auf die u / v-Ebenen ist uint uvIndex = (...) Ich denke, es könnte das Problem geben, wahrscheinlich mit falscher Berücksichtigung von Pixelstrides hier. Kennt jemand die Lösung? Vielen Dank

UPDATE: Ich habe alles überprüft und bin mir ziemlich sicher, dass der Code für den Zugriff von y, u, v korrekt ist. Das Problem muss also bei den Werten u und v selbst liegen. Nichtgrüne Farben haben eine violette Neigung und bei Betrachtung der u, v-Werte scheinen sie in einem ziemlich engen Bereich von etwa 110-150 zu liegen. Ist es wirklich möglich, dass wir gerätespezifische YUV -> RBG-Konvertierungen bewältigen müssen ...?! Habe ich etwas vergessen

UPDATE 2: Code korrigiert, funktioniert jetzt dank Eddys Feedback.

Antworten auf die Frage(10)

Ihre Antwort auf die Frage