ho scritto una conversione da YUV_420_888 a Bitmap, considerando la seguente logica (se ho capito bene):YUV_420_888 interpretazione sul Samsung Galaxy S7 (Camera2)
In sintesi l'approccio: del kernel coordinate x e y sono congruenti sia con la xey della parte non imbottita del Y-Plane (allocazione 2d) e la xey della bitmap di output. I piani U e V, tuttavia, hanno una struttura diversa rispetto al piano Y, perché usano 1 byte per una copertura di 4 pixel e, in aggiunta, possono avere un PixelStride che è più di uno, inoltre potrebbero hanno anche un riempimento che può essere diverso da quello del piano Y. Pertanto, per accedere in modo efficiente a U e V dal kernel, li ho inseriti in allocazioni 1-d e creato un indice "uvIndex" che fornisce la posizione dei corrispondenti U- e V all'interno dell'allocazione 1-d, per data (x, y) coordinate nel piano Y (non imbottito) (e, quindi, il bitmap di output).
Al fine di mantenere il rs-Kernel snello, ho escluso l'area di padding nella yPlane limitando l'intervallo x tramite LaunchOptions (ciò riflette il RowStride del piano y che quindi può essere ignorato ENTRO il kernel). Quindi dobbiamo solo considerare uvPixelStride e uvRowStride all'interno di uvIndex, cioè l'indice utilizzato per accedere ai valori u e v.
Questo è il mio codice:
Renderscript Kernel, yuv420888.rs nome
#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;
}
lato 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, 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;
}
test sui Nexus 7 (API 22) questo restituisce bel colore Bitmap . Questo dispositivo, tuttavia, ha triviali pixeltrides (= 1) e nessun padding (cioè rowstride = width). Provando sul nuovissimo Samsung S7 (API 23) ottengo immagini i cui colori non sono corretti - tranne quelli verdi. Ma l'immagine non mostra una tendenza generale verso il verde, sembra solo che i colori non verdi non vengano riprodotti correttamente. Notare che S7 applica un pixel verticale su 2 e nessun riempimento.
Poiché la riga di codice più importante è all'interno del codice rs l'accesso degli u/v planes uint uvIndex = (...) Penso che ci potrebbe essere il problema, probabilmente con una considerazione errata di pixelstrides qui. Qualcuno vede la soluzione? Grazie.
AGGIORNAMENTO: ho controllato tutto, e sono abbastanza sicuro che il codice relativo all'accesso di y, u, v sia corretto. Quindi il problema deve essere con i valori u e v stessi. I colori non verdi hanno un'inclinazione viola, e osservando i valori di u, sembrano essere in un intervallo piuttosto ristretto di circa 110-150. È davvero possibile che dobbiamo affrontare le conversioni YUV -> RBG specifiche del dispositivo ...?! Mi sono perso qualcosa?
UPDATE 2: ha corretto il codice, ora funziona, grazie a Feedback di Eddy.
assegnandomi RSIllegalArgumentException: Array troppo piccolo per il tipo di allocazione. on line "yAlloc.copyFrom (y);" qualche idea. Il telefono di prova è Nexus 6p – Umair
quali valori hai per lunghezza [y], yRowStride, larghezza e altezza? – Settembrini
@Settembrini Sto usando questo codice e funziona alla grande sulla maggior parte dei dispositivi, ma su Pixel viene lanciato 'android.support.v8.renderscript.RSIllegalArgumentException: Array troppo piccolo per tipo di allocazione. Come lo risolvo? Impossibile trovare molto su Google – Ryan