2010-03-14 339 views
21

我已经得到了16位rgb565图像(具体来说,Android帧缓冲转储)的手,我想将它转换为24位rgb888以便在普通显示器上查看。如何将16位RGB565转换为24位RGB888?

问题是,如何将5或6位通道转换为8位?显而易见的答案是改变它。我开始写这个:

puts("P6 320 480 255"); 
uint16_t buf; 
while (read(0, &buf, sizeof buf)) { 
    unsigned char red = (buf & 0xf800) >> 11; 
    unsigned char green = (buf & 0x07e0) >> 5; 
    unsigned char blue = buf & 0x001f; 
    putchar(red << 3); 
    putchar(green << 2); 
    putchar(blue << 3); 
} 

然而,这并不有一个属性,我想,这是0xffff映射,而不是0xf8fcf80xffffff。我需要以某种方式扩大价值,但我不确定这应该如何工作。

Android SDK带有一个名为ddms(Dalvik Debug Monitor)的工具,它可以捕获屏幕。据我从reading the code可以看出,它实现了相同的逻辑;但其截图出现了不同,白色映射到白色。

这里的raw framebuffer,smart conversion ddms和dumb conversion通过上述算法。请注意后者稍暗,更绿。

(顺便说一句,这种转换is implemented in ffmpeg,但它只是执行上面列出的哑转换,在所有零离开的LSB)

我想我有两个问题:

  • 有什么最明智的方式将rgb565转换为rgb888?
  • DDMS如何转换其截图?

回答

16

你可以转移,然后或与最重要的位;即

Red 10101 becomes 10101000 | 101 => 10101101 
    12345   12345--- 123 12345123 

这具有您寻找的属性,但它不是从一个空间到另一个空间的值的最线性映射。虽然这很快。 :)

Cletus的答案更完整,可能更好。 :)

+1

是的,这正是他们正在做的。我认为这实际上是*最*线性映射。 – 2010-03-14 15:37:08

+1

截断乘以8.25,而截尾乘以不同的舍入值。无论哪种方式,有7个“步骤”,在该步骤中,将1加到输入上,而不是8中输出。我想你可以根据步骤发生的位置来定义“缺少线性” - 8段输入,也许?或者计算所有32个输入与其输出的相关系数,最大结果为“最线性”。 – 2010-03-14 16:37:48

21

您想将每个从5/6位空间映射到8位空间。

  • 5位= 32个值
  • 6位= 64个值
  • 8位= 256个值

您使用走的是幼稚方法的代码,X5 *32分之256 = x8其中256/32 = 8并乘以8左移3,但正如你所说,这并不一定能够“正确地”填充新的数字空间。最大值为5到8是31到255,这是你解决问题的线索。

x8 = 255/31 * x5 
x8 = 255/63 * x6 

其中x5x6x8是5,分别6和8位的值。

现在有一个关于实现这个最好方法的问题。它确实涉及到除法和整数除法,你将失去任何余数结果(基本上舍入),所以最好的解决方案可能是做浮点算术,然后回到整数一半。

只需使用此公式为5位和6位转换中的每一个生成查找表即可大幅提高速度。

+2

刚刚进行了一次测试,这相当于重复使用前3位,但有时它会增加或减少1.我认为这没有真正的优势。 – 2010-03-14 15:35:31

+0

http://codepad.org/qLvbkTO3这就是它的样子;底部的3位大部分位于前3位。我认为唯一可能的方式是让某个方法更加巧妙地颠倒使用过的抖动。 – 2010-03-14 15:41:50

+0

“加减1”是重要的部分。不这样做往往会放大16位颜色产生的低精度带状伪影。 – Alan 2010-03-14 16:44:34

4

试试这个:

red5 = (buf & 0xF800) >> 11; 
red8 = (red5 << 3) | (red5 >> 2); 

这将全零映射到全部为零,全部为1到全1,以及之间的一切成之间的一切。你可以把它更有效地通过比特移位到在一个步骤:

redmask = (buf & 0xF800); 
rgb888 = (redmask << 8) | ((redmask<<3)&0x070000) | /* green, blue */ 

做同样为绿色和蓝色(为6位,分别移在顶部方法左2,右4)。

+0

是的,这是解决方案(但用C表示,我们还没有)。除了你打算除以8或右移3。 – 2010-03-15 05:41:12

+0

哎呦 - 半睡半醒时不要写这些东西。现在修复。另外,应该注意到冷静的解决方案做同样的事情。 – 2010-03-15 13:07:12

2

一般的解决方案是将数字视为二进制分数 - 因此,6位数字63/63与8位数字255/255相同。您可以使用最初的浮点数学计算此值,然后计算查找表,如其他海报所示。这也比直击式解决方案更直观。 :)

3

有一个错误jleedev!

unsigned char green = (buf & 0x07c0) >> 5; 
unsigned char blue = buf & 0x003f; 

良好的代码

unsigned char green = (buf & 0x07e0) >> 5; 
unsigned char blue = buf & 0x001f; 

干杯, 安迪

+0

好的。不过,我认为这只是编写问题时的一个错字,因为我笔记本电脑上的代码是正确的。 – 2010-10-21 20:36:10

17

我几毛钱:

如果你关心精确测绘,又好又快的算法可以这样考虑:

R8 = (R5 * 527 + 23) >> 6; 
G8 = (G6 * 259 + 33) >> 6; 
B8 = (B5 * 527 + 23) >> 6; 

它只使用:MUL,ADD和SHR - >所以它非常快! 从另一面它是在100%与适当的四舍五入浮点映射兼容:

// R8 = (int) floor(R5 * 255.0/31.0 + 0.5); 
// G8 = (int) floor(G6 * 255.0/63.0 + 0.5); 
// B8 = (int) floor(R5 * 255.0/31.0 + 0.5); 

一些额外的仙: 如果你有兴趣在888至565的转换,这个作品非常好太:

R5 = (R8 * 249 + 1014) >> 11; 
G6 = (G8 * 253 + 505) >> 10; 
B5 = (B8 * 249 + 1014) >> 11; 

常量被发现使用强制搜索与somę早期拒绝加快了一点东西。

+4

你是如何计算这些值的? (我想对其他频道深度使用相同的技巧。) – 2013-04-05 13:52:21

+0

要迂腐,floor(x + 0.5)与round(x)不是一回事,而实际上你需要后者。 floor(x + 0.5)为0.5f-1ulp做了错误的事情,它会向上滚动。 0.5f-1ulp不会在这里发生。您可以通过从最后想要做的右移开始(例如在这种情况下为64)开始计算这样的值,并通过比例因子(255/31)乘以它。你得到255 * 64/31 = 526.45。这是一个近似值。幸运的是,这并不是很多数字。所以,如果你足够接近,你通常可以得到正确的结果。 – 2015-03-30 17:18:06

0

我用下面的方法得到了很好的结果。原来我的Logitek摄像头是16bit RGB555,使用以下命令转换为24bit RGB888,允许我使用较小的动物ijg保存为jpeg:感谢您在这里找到的提示信息。

// Convert a 16 bit inbuf array to a 24 bit outbuf array 
BOOL JpegFile::ByteConvert(BYTE* inbuf, BYTE* outbuf, UINT width, UINT height) 
{  UINT row_cnt, pix_cnt;  
     ULONG off1 = 0, off2 = 0; 
     BYTE tbi1, tbi2, R5, G5, B5, R8, G8, B8; 

     if (inbuf==NULL) 
      return FALSE; 

     for (row_cnt = 0; row_cnt <= height; row_cnt++) 
     {  off1 = row_cnt * width * 2; 
      off2 = row_cnt * width * 3; 
      for(pix_cnt=0; pix_cnt < width; pix_cnt++) 
      { tbi1 = inbuf[off1 + (pix_cnt * 2)]; 
       tbi2 = inbuf[off1 + (pix_cnt * 2) + 1]; 
       B5 = tbi1 & 0x1F; 
       G5 = (((tbi1 & 0xE0) >> 5) | ((tbi2 & 0x03) << 3)) & 0x1F; 
       R5 = (tbi2 >> 2) & 0x1F; 
       R8 = (R5 * 527 + 23) >> 6; 
       G8 = (G5 * 527 + 23) >> 6; 
       B8 = (B5 * 527 + 23) >> 6; 
       outbuf[off2 + (pix_cnt * 3)] = R8; 
       outbuf[off2 + (pix_cnt * 3) + 1] = G8; 
       outbuf[off2 + (pix_cnt * 3) + 2] = B8; 
      } 
     } 
     return TRUE; 
}   
+0

这个问题是关于5 ** 6 ** 5的问题,所以对于绿色和527绿色不起作用。 – TWiStErRob 2016-04-09 17:37:19

+0

'row_cnt <= height'子句错了吗?它不应该是'row_cnt <高度'? – 2017-11-20 15:05:43

5

的iOS VIMAGE转换

iOS Accelerate Framework证件办理vImageConvert_RGB565toARGB8888功能如下算法:

Pixel8 alpha = alpha 
Pixel8 red = (5bitRedChannel * 255 + 15)/31 
Pixel8 green = (6bitGreenChannel * 255 + 31)/63 
Pixel8 blue = (5bitBlueChannel * 255 + 15)/31 

对于一次性转换,这将是速度不够快,但如果你要处理许多帧你想使用像iOS vImage转换或自己实现这个使用NEON intrinsics

从华军社区论坛教程

首先,我们将着眼于转换RGB565 RGB888来。我们假设寄存器q0中有8个16位像素,我们希望将红色,绿色和蓝色分成三个寄存器d2到d4的8位元素。

vshr.u8  q1, q0, #3  @ shift red elements right by three bits, 
           @ discarding the green bits at the bottom of 
           @ the red 8-bit elements. 
vshrn.i16 d2, q1, #5  @ shift red elements right and narrow, 
           @ discarding the blue and green bits. 
vshrn.i16 d3, q0, #5  @ shift green elements right and narrow, 
           @ discarding the blue bits and some red bits 
           @ due to narrowing. 
vshl.i8  d3, d3, #2  @ shift green elements left, discarding the 
           @ remaining red bits, and placing green bits 
           @ in the correct place. 
vshl.i16 q0, q0, #3  @ shift blue elements left to most-significant 
           @ bits of 8-bit color channel. 
vmovn.i16 d4, q0   @ remove remaining red and green bits by 
           @ narrowing to 8 bits. 

每个指令的效果在上述评论中描述,但概括地说,在每个通道上执行的操作是: 用于使用移动到关闭或者推位相邻通道除去颜色数据元素的结尾。 使用第二次移位将颜色数据定位在每个元素的最高有效位中,然后缩小以将元素大小从16位减少到8位。

注意使用这个序列中的元素大小来寻址8位和16位元素,以实现一些掩码操作。

一个小问题

你可能会注意到,如果你使用上面的代码转换为RGB888格式,你的白人是不是很白很白。这是因为,对于每个通道,最低的两个或三个比特是零,而不是一个;在RGB888中,以RGB565表示的白色(0x1F,0x3F,0x1F)变为(0xF8,0xFC,0xF8)。这可以通过使用插入移位来固定,以将一些最重要的位放入较低位。

对于Android特定示例,我找到了一个用intrinsics编写的YUV-to-RGB conversion