尝试编写一种有效的算法,将YUV 4:2:2按比例缩小2倍 - 并且不需要转换为RGB(这是CPU密集型)。
我已经在YUV到RGB转换的堆栈溢出上看到了大量代码 - 但这里只是YUV 4:2:0的缩放示例,我已经开始根据我的代码开始了。然而,这产生的图像实际上是具有损坏颜色的相同图像的3列,因此当应用于4:2:2时,算法出现问题。
有人能看出这段代码有什么问题吗?
public static byte[] HalveYuv(byte[] data, int imageWidth, int imageHeight)
{
byte[] yuv = new byte[imageWidth / 2 * imageHeight / 2 * 3 / 2];
int i = 0;
for (int y = 0; y < imageHeight; y += 2)
{
for (int x = 0; x < imageWidth; x += 2)
{
yuv[i] = data[y * imageWidth + x];
i++;
}
}
for (int y = 0; y < imageHeight / 2; y += 2)
{
for (int x = 0; x < imageWidth; x += 4)
{
yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x];
i++;
yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x + 1)];
i++;
}
}
return yuv;
}
答案 0 :(得分:1)
生成低质量缩略图的一种快速方法是丢弃每个维度中的一半数据。
我们在4x2像素网格中打破图像 - 网格中的每对像素由4个字节表示。在缩小的图像中,我们通过复制前4个字节来获取网格中前2个像素的颜色值,同时丢弃其他12个字节的数据。
这种缩放可以推广到2(1 / 2,1 / 4,1 / 8,...)的任何幂 - 这种方法很快,因为它没有使用任何插值。这样可以提供质量较低的图像,但看起来很块 - 为了更好的结果,可以考虑一些采样方法。
public static byte[] FastResize(
byte[] data,
int imageWidth,
int imageHeight,
int scaleDownExponent)
{
var scaleDownFactor = (uint)Math.Pow(2, scaleDownExponent);
var outputImageWidth = imageWidth / scaleDownFactor;
var outputImageHeight = imageHeight / scaleDownFactor;
// 2 bytes per pixel.
byte[] yuv = new byte[outputImageWidth * outputImageHeight * 2];
var pos = 0;
// Process every other line.
for (uint pixelY = 0; pixelY < imageHeight; pixelY += scaleDownFactor)
{
// Work in blocks of 2 pixels, we discard the second.
for (uint pixelX = 0; pixelX < imageWidth; pixelX += 2*scaleDownFactor)
{
// Position of pixel bytes.
var start = ((pixelY * imageWidth) + pixelX) * 2;
yuv[pos] = data[start];
yuv[pos + 1] = data[start + 1];
yuv[pos + 2] = data[start + 2];
yuv[pos + 3] = data[start + 3];
pos += 4;
}
}
return yuv;
}
答案 1 :(得分:1)
我假设原始数据的顺序如下(从您的示例代码看起来如此):首先是图像像素的亮度(Y)值(size = imageWidth*imageHeight
字节)。之后,存在色度分量UV,s.t。,单个像素的值彼此相继给出。这意味着原始图片的总大小为3*size
。
现在4:2:2子采样意味着丢弃水平色度分量的每个其他值。这将数据减小到大小size + 0.5*size + 0.5*size = 2*size
,即,完全保持亮度,并且两个色度分量被分成一半。因此,结果图像应分配为:
byte[] yuv = new byte[2*imageWidth*imageHeight];
当图像的第一部分被完整复制时,第一个循环变为:
int i = 0;
for (int y = 0; y < imageHeight; y++)
{
for (int x = 0; x < imageWidth; x++)
{
yuv[i] = data[y * imageWidth + x];
i++;
}
}
因为这只是复制数据的开头,所以可以简化为
int size = imageHeight*imageWidth;
int i = 0;
for (; i < size; i++)
{
yuv[i] = data[i];
}
现在要复制其余部分,我们需要跳过所有其他水平坐标
for (int y = 0; y < imageHeight; y++)
{
for (int x = 0; x < imageWidth; x += 2) // +2 skip each other horizontal component
{
yuv[i] = data[size + y*2*imageWidth + 2*x];
i++;
yuv[i] = data[size + y*2*imageWidth + 2*x + 1];
i++;
}
}
需要data
中的因子2 - 数组索引,因为每个像素(两个色度分量)都有2个字节,所以每个&#34;行&#34;有2*imageWidth
个字节的数据。