【问题标题】:How to Convert a 12-bit Image to 8-bit in C/C++?如何在 C/C++ 中将 12 位图像转换为 8 位?
【发布时间】:2011-04-24 15:44:51
【问题描述】:

好吧,所以我一直非常沮丧尝试将 12 位缓冲区转换为 8 位缓冲区。 图像源是 12 位灰度(从 JPEG2000 解压缩),其颜色范围为 0-4095。现在我必须将其减少到 0-255。常识告诉我,我应该像这样简单地划分每个像素值。但是当我尝试这个时,图像太亮了。

void 
TwelveToEightBit(
    unsigned char * charArray,
    unsigned char * shortArray,
    const int num )
{

    short shortValue  = 0; //Will contain the two bytes in the shortArray.
    double doubleValue  = 0; //Will contain intermediary calculations.

    for( int i = 0, j =0; i < num; i++, j +=2 )
    {
        // Bitwise manipulations to fit two chars onto one short.
        shortValue = (shortArray[j]<<8);
        shortValue += (shortArray[j+1]);

        charArray[i] = (( unsigned char)(shortValue/16));
    }
}

现在我可以看出需要进行一些对比度调整。有什么想法吗?

提前致谢

【问题讨论】:

  • 为什么 shortArray 被声明为 unsigned char * 而不是更方便的 unsigned* 类型?
  • 您可能会尝试使用伽马校正:类似 pow(double(pixel)/16, gamma)/pow(255,gamma) - 伽马为 1.1 且因口味而异。
  • @DarenW :因为我使用的任何阅读器一次读取一个字节,就像大多数阅读器一样。但是,它包含的空间是 charBuffer 的两倍,因为它必须容纳两倍的字节数。
  • @Tony:是的,我认为这与我最终所做的非常相似。我一定会试一试,看看它会把我带到哪里。

标签: c++ c image colors


【解决方案1】:

大胆猜测:您的代码假定为大端机器(最高有效字节在前)。 Windows PC 是小端的。所以也许试试

  shortValue = (shortArray[j+1]<<8);
  shortValue += (shortArray[j]);

如果确实是 endiasness 问题,那么您提供的代码只会删除每个值的 4 个最高有效位,并将其余部分扩展到强度范围。嗯,编辑,2 秒后:不,那是一个想法。不过还是试试吧?

干杯,

--阿尔夫

【讨论】:

    【解决方案2】:

    据我了解,主要问题是将 12 位值转换为 8 位值。

    Range of 12-bit value = 0 - 4095 (4096 values)
    Range of  8-bit value = 0 -  255 ( 256 values)
    

    我会尝试将 12 位值 x 转换为 8 位值 y

    1. 首先,先缩小到 0-1 的范围,然后
    2. 然后,放大到 0-256 范围。

    一些 C-ish 代码:

    uint16_t x = some_value; 
    uint8_t  y = (uint8_t) ((double) x/4096 ) * 256;
    

    更新

    感谢Kriss 的评论,我意识到我忽略了速度问题。由于浮点运算,上述解决方案可能比纯整数运算慢。

    然后我开始考虑另一种解决方案。用x 的最高有效位8 构造y 怎么样?换句话说,通过修剪掉4 的最低有效位。

    y = x >> 4;
    

    这行得通吗?

    【讨论】:

    • 我认为你已经接近或在这个钱上。
    • 原始代码已经以更有效的方式执行类似的操作,并且不会因舍入而失去比必要的精度更高的精度(但可能存在字节顺序问题?)。除了通过执行浮点除法使代码变慢或变差之外,您的建议根本没有任何好处。
    • @Juan:谢谢,但我不确定这是鼓励还是讽刺 :-)。 @kriss:感谢您指出缺点。我发布了更新,如果您还有其他 cmets,请告诉我。 @匿名投票者:我正在尝试通过解决来学习。如果您像 @kriss 那样提供您的 cmets,这对我会有帮助。
    • @ArunSaha:没有讽刺,当您说将值缩放到 0-1 范围时,我觉得您触动了一些东西。其余的都是多余的,但你的第一句话就打了折扣。
    • @Juan:谢谢。我的(更新的)答案有意义吗?
    【解决方案3】:

    实际上,只是需要进行一些简单的对比度调整。当我在 Photoshop 中加载结果图像并进行自动对比时,我就意识到了这一点……图像结果与预期的输出图像非常相似。 我发现了一种可以进行对比的算法,并将其发布在此处以方便其他人使用:

    #include <math.h>
    
     short shortValue  = 0; //Will contain the two bytes in the shortBuffer.
     double doubleValue  = 0; //Will contain intermediary calculations.
    
     //Contrast adjustment necessary when converting
     //setting 50 as the contrast seems to be real sweetspot.
     double contrast = pow( ((100.0f + 50.0f) / 100.0f), 2); 
    
     for ( int i = 0, j =0; i < num; i++, j += 2 )
     {
    
      //Bitwise manipulations to fit two chars onto one short.
      shortValue = (shortBuffer[j]<<8);
      shortValue += (shortBuffer[j+1]);
    
      doubleValue = (double)shortValue;
    
      //Divide by 16 to bring down to 0-255 from 0-4095 (12 to 8 bits)
      doubleValue /= 16;
    
      //Flatten it out from 0-1
      doubleValue /= 255;
      //Center pixel values at 0, so that the range is -0.5 to 0.5
      doubleValue -= 0.5f;
      //Multiply and just by the contrast ratio, this distances the color
      //distributing right at the center....see histogram for further details
      doubleValue *= contrast;
    
      //change back to a 0-1 range
      doubleValue += 0.5f;
      //and back to 0-255
      doubleValue *= 255;
    
    
      //If the pixel values clip a little, equalize them.
      if (doubleValue >255)
       doubleValue = 255;
      else if (doubleValue<0)
       doubleValue = 0;
    
      //Finally, put back into the char buffer.
      charBuffer[i] = (( unsigned char)(doubleValue));
    
    
     }
    

    【讨论】:

    • 又好又简单 :-)。给你一个线性拉伸的对比度增强,而伽玛的东西是指数的。线性显然更容易快速计算。如果您的图像已经跨越整个动态范围,则两者都存在重新缩放以避免高光/阴影丢失的问题。
    • @Tony:你不知道我有多担心我将不得不实现像快速傅立叶变换或其他一些复杂算法这样可怕的东西。这确实是一种线性方法,但听起来指数方法可能会更好地分散集中的像素值,但因为我们在这里处理的是 0-255 的紧密光谱,所以它可能不那么明显。非常感谢您的帮助!真的很感激!
    • 这是一个棘手的空间 - 要获得真正好的结果,您通常需要手动调整 - 在 Photoshop 中,我倾向于使用多点曲线调整:它们确实会通过您添加的控制点创建平滑曲线而且我敢肯定数学会涉及更多。基本上,对于您想要增加对比度的任何地方,您都必须接受您正在降低强度级别的其他部分的对比度。使用 12 位输入,您将获得不那么明显的条带,但输出强度的差异仍然是真实的。但是,如果你所拥有的对你的目的来说足够好,那就太好了!
    【解决方案4】:

    如果您只想删除底部的 4 个最低有效位,您可以执行以下操作:

    unsigned int start_value = SOMEVALUE; // starting value
    value = (value & 0xFF0 );             // drop bits 
    unsigned char final_value =(uint8_t)value >> 4; //bit shift to 8 bits
    

    注意“未签名”。您不希望有符号位与您的值混淆。

    【讨论】:

      【解决方案5】:

      像这样:

      // Image is stored in 'data'
      unsigned short* I = (unsigned short*)data;
      
      for(int i=0; i<imageSize; i++) {
      // 'color' is the 8-bit value  
         char color = (char)((double)(255*I[i])/(double)(1<<12));
         /*...*/ 
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-07
        • 2016-10-09
        • 2012-05-11
        • 2014-10-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多