首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >三星Galaxy S7 (Camera2)的S7解读

三星Galaxy S7 (Camera2)的S7解读
EN

Stack Overflow用户
提问于 2016-03-25 01:56:04
回答 8查看 14.1K关注 0票数 33

考虑到以下逻辑(据我理解),我编写了从YUV_420_888到Bitmap的转换:

概括地说,核坐标x和y与Y平面的非填充部分(2d-分配)的x和y以及输出位图的x和y是一致的。然而,U-和V-平面的结构与Y平面不同,因为它们使用1字节来覆盖4个像素,此外,可能有一个超过一个的PixelStride,此外,它们还可能有一个与Y平面不同的填充。因此,为了通过内核有效地访问U和V,我将它们放在一维分配中,并创建了一个索引“uvIndex”,它给出了相应的U-和V在该一维分配中的位置,即给定的(x,y)坐标在(非填充的)Y平面(以及,因此,输出位图)。

为了保持rs-内核的精瘦,我通过通过yPlane限制x-范围(这反映了y-平面的RowStride,因此可以在内核中忽略)而排除了内核中的填充区域。因此,我们只需要考虑uvPixelStride和uvRowStride在uvIndex中的作用,即用来访问u和v值的索引。

这是我的密码:

Renderscript内核,名为yuv420888.rs

代码语言:javascript
复制
  #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侧:

代码语言:javascript
复制
    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));

    //using safe height
    typeUcharY.setX(yRowStride).setY(y.length / yRowStride);

    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
    //using safe height
    lo.setY(0, y.length / yRowStride);

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

    return outBitmap;
}

在Nexus 7 (API 22)上进行测试,这会返回漂亮的彩色位图。然而,该设备具有微不足道的像素步幅(=1)和无填充(即rowstride=width)。在全新的三星S7 (API 23)上测试,我得到了颜色不正确的图片--除了绿色的。但这幅画并没有表现出对绿色的普遍偏见,似乎非绿色的颜色没有得到正确的再现。请注意,S7应用的u/v像素步长为2,没有填充。

由于最关键的代码行在rs-代码中,所以u/v平面uint uvIndex= (.)的访问我认为,这可能是个问题,可能是因为这里对像素的考虑不正确。有人看到解决办法了吗?谢谢。

更新:我检查了所有内容,并且非常肯定关于y,u,v的访问的代码是正确的。所以问题必须是u和v值本身。非绿色的颜色有一个紫色的倾斜,看看u,v值,他们似乎在一个相当狭窄的范围约110-150。我们真的有可能需要处理特定于设备的YUV -> RBG转换吗?!我错过什么了吗?

更新2:已经修正了代码,它现在工作了,多亏了Eddy的反馈。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2016-03-25 19:25:43

代码语言:javascript
复制
floor((float) uvPixelStride*(x)/2)

它从Y坐标计算U,V行偏移量(uv_row_offset) .

如果uvPixelStride = 2,那么随着x的增加:

代码语言:javascript
复制
x = 0, uv_row_offset = 0
x = 1, uv_row_offset = 1
x = 2, uv_row_offset = 2
x = 3, uv_row_offset = 3

这是不正确的。在uv_row_offset =1或3处没有有效的U/V像素值,因为uvPixelStride = 2。

你想要的

代码语言:javascript
复制
uvPixelStride * floor(x/2)

(假设您不相信自己记得整数除法的关键整和行为,如果这样做的话):

代码语言:javascript
复制
uvPixelStride * (x/2)

应该就够了

这样,您的映射就变成了:

代码语言:javascript
复制
x = 0, uv_row_offset = 0
x = 1, uv_row_offset = 0
x = 2, uv_row_offset = 2
x = 3, uv_row_offset = 2

看看这是否修复了颜色错误。实际上,这里不正确的寻址意味着其他颜色样本都来自错误的颜色平面,因为潜在的YUV数据很可能是半平面的(因此U平面从V平面+1字节开始,两个平面相互交错)

票数 9
EN

Stack Overflow用户

发布于 2016-12-21 11:09:03

对于那些遇到错误的人

代码语言:javascript
复制
android.support.v8.renderscript.RSIllegalArgumentException: Array too small for allocation type

使用buffer.capacity()而不是buffer.remaining()

如果您已经对图像进行了一些操作,则需要在缓冲区上调用rewind()方法。

票数 5
EN

Stack Overflow用户

发布于 2017-02-02 09:27:28

另外,对于其他人来说

android.support.v8.renderscript.RSIllegalArgumentException:数组太小,不适合分配类型

我通过将yAlloc.copyFrom(y);更改为yAlloc.copy1DRangeFrom(0, y.length, y);来修复它

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36212904

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档