我编写了一个从YUV_420_888到位图的转换,考虑了以下逻辑(根据我的理解):
总结一下这个方法:内核的坐标x和y与y平面(2D-分配)的非填充部分的x和y以及输出位图的x和y是一致的。然而,U平面和V平面具有不同于Y平面的结构,因为它们使用1字节来覆盖4个像素,此外,它们可能具有超过一个像素步幅,此外,它们还可能具有与Y平面不同的填充。因此,为了让内核有效地访问U和V,我将它们放入一维分配中,并创建了一个索引“uvindex”,它给出了在一维分配中对应的U和V的位置,对于(非填充的)y平面(以及输出位图)中给定的(x,y)坐标。
为了保持RS内核的精简,我通过LaunchOptions限制X范围,从而排除了yPlane中的填充区域(这反映了Y平面的延伸,因此在内核中可以忽略)。因此,我们只需要考虑uvIndex中的uvPixelStride和uvRowStride,即用于访问U值和V值的索引。
Renderscript内核,名为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端:
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;
}
在Nexus7(API22)上测试,这将返回漂亮的彩色位图。然而,该设备具有微不足道的pixelstrides(=1),并且没有填充(即rowstride=width)。在brandnew Samsung S7(API23)上测试,我得到的图片颜色不正确--除了绿色的。但是这张图片并没有显示出对绿色的普遍偏见,只是看起来非绿色的颜色没有被正确地复制。注意,S7应用的U/V像素步幅为2,并且没有填充。
看
floor((float) uvPixelStride*(x)/2)
它从Y x坐标计算U,V行偏移量(uv_row_offset)。
如果uvPixelStride=2,则随着x的增加:
x = 0, uv_row_offset = 0
x = 1, uv_row_offset = 1
x = 2, uv_row_offset = 2
x = 3, uv_row_offset = 3
uvPixelStride * floor(x/2)
uvPixelStride * (x/2)
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字节开始,两个平面交错)
问题内容: 我正在使用OnImageAvailableListener获得预览帧: 每次数据长度不同,但图像的宽度和高度相同。 主要问题:对于3264x2448之类的分辨率而言太小。 数据数组的大小应为3264 * 2448 = 7,990,272,而不是300,000-600,000。 怎么了? 问题答案: 我通过使用 YUV_420_888 图像格式并将其手动转换为 JPEG 图像格式解决了此
为什么两个函数调用的结果是不同的,而请求的类型是相同的? 更新:正如注释中提到的,我得到这种行为是因为不同的图像大小(捕获请求的尺寸要大得多)。但是我们在JNI端的进一步处理操作对于两个请求是相同的,并且不依赖于图像尺寸(只依赖于纵横比,这在两种情况下是相同的)。
我试图将YUV_420_888图像转换为位图,来自camera2预览。但是输出图像的颜色不正确。 请让我知道如果有人能发现转换的问题:
我正在编写一个简单的Android应用程序,在该应用程序中,我使用ImageReader.onImageAvailableListener检索帧缓冲区,以获取最新的图像并处理Y平面。我使用这个平面来计算一个简单的度量,它的值将决定我是否在屏幕上显示某些东西。 我的问题是如何只处理和解释Y平面(我不需要U和V平面)。根据我的理解,Y平面包含亮度通道;即图像亮度的通道。 我的问题是,这些亮度值应该如何
感谢信+1 小飞狗走喽 ————————分割线————————— 写面经攒人品^_^ 看到牛牛上广州三星的面经好少,就来写一个吧~ 8月末投的,11月15号一面,我不知道是开的晚还是把我捞起来了 1.自我介绍 2.测试过程 3.测试中重要的阶段 4.职业规划(啥问题我忘了,当时我没听懂,面试官说大概就是职业规划,那第四问就是职业规划) 5.找工作比较看重公司的什么方面 没了。。。十分钟结束战斗,大
1小时 1min自我介绍 2min看题 30min小组讨论 题目中规中矩,不算有难度,同组的人大都是学市场营销和产品的 没啥人抢 一个人跳出来当记录员,共享了屏幕实时记录,最后也是他汇报的 另外还有两三个人比较积极的说 我很水,想起来就补充一下 5min汇报 最后hr谢谢大家就结束了,也没有提问或是总结,有点随意 最后还留下了三个人,单独问,不知道啥意思#三星##三星面试##群面#