【问题标题】:How to get the calculate the RGB values of a pixel from the luminance?如何从亮度中计算像素的 RGB 值?
【发布时间】:2020-07-08 20:47:27
【问题描述】:

我想根据亮度计算 RGB 值。

我知道的数据是:

  • 新的亮度(我要应用的值)
  • 旧照度
  • 旧的 RGB 值。

我们可以像这样从 RGB 值计算亮度:
uint8_t luminance = R * 0.21 + G * 0.71 + B * 0.07;

我的代码是:

// We create a function to set the luminance of a pixel
void jpegImage::setLuminance(uint8_t newLuminance, unsigned int x, unsigned int y) {

  // If the X or Y value is out of range, we throw an error
  if(x >= width) {
    throw std::runtime_error("Error : in jpegImage::setLuminance : The X value is out of range");
  }
  else if(y >= height) {
    throw std::runtime_error("Error : in jpegImage::setLuminance : The Y value is out of range");
  }

  // If the image is monochrome
  if(pixelSize == 1) {

    // We set the pixel value to the luminance
    pixels[y][x] = newLuminance;
  }

  // Else if the image is colored, we throw an error
  else if(pixelSize == 3) {
    // I don't know how to proceed
    // My image is stored in a std::vector<std::vector<uint8_t>> pixels;

    // This is a list that contain the lines of the image
    // Each line contains the RGB values of the following pixels
    // For example an image with 2 columns and 3 lines
    // [[R, G, B, R, G, B], [R, G, B, R, G, B], [R, G, B, R, G, B]]

    // For example, the R value with x = 23, y = 12 is:
    // pixels[12][23 * pixelSize];
    // For example, the B value with x = 23, y = 12 is:
    // pixels[12][23 * pixelSize + 2];
    // (If the image is colored, the pixelSize will be 3 (R, G and B)
    // (If the image is monochrome the pixelSIze will be 1 (just the luminance value)
  }
}

我该如何继续? 谢谢!

【问题讨论】:

  • 您不能仅从亮度计算颜色。您需要三个属性,如Hue + Saturation + Luminance。您是在问“我怎样才能使颜色变亮或变暗”?
  • 您应该注意,“亮度”具有精确的含义;它是到达观察者眼睛的光量,以每平方米坎德拉为单位(非正式地称为“尼特”)。这就是你所说的“亮度”吗?可能不会,因为通常“RGB 像素”没有这种亮度。此外,RGB 有很多版本,而不仅仅是 sRGB。

标签: c++ math libjpeg


【解决方案1】:

如果你有原始的 RGB,你就不需要旧的亮度。

引用 https://www.fourcc.org/fccyvrgb.php 进行 YUV 到 RGB 的转换。

从原始 RGB 计算 U 和 V:

```
V =  (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
```

Y 是归一化为 0 到 255 之间的值的新亮度

然后只需转换回 RGB:

B = 1.164(Y - 16)                   + 2.018(U - 128)
G = 1.164(Y - 16) - 0.813(V - 128) - 0.391(U - 128)
R = 1.164(Y - 16) + 1.596(V - 128)

确保将每个方程的计算值限制在0..255 的范围内。其中一些公式可以将 YUV 或 RGB 值转换为小于 0 或大于 255 的值。

YUV 和 RGB 之间的转换也有多种公式。 (不同的常数)。我注意到上面列出的页面对 Y 的计算与您引用的不同。它们都相对接近,具有不同的精度和调整。仅仅改变一个像素的亮度,几乎任何公式都可以。

更新

在 OP 建议它对他不起作用后,我最初删除了这个答案。最近几天忙得没时间调查,但我写了一些示例代码来证实我的假设。在这个答案的底部是基于 GDI+ 的代码的 sn-p 将图像的亮度增加了一个变量。与代码一起是我测试过的图像和两次转换。一个亮度为 130%。另一个亮度为 170%。

这是一个转换示例

原图

更新后的图片(130% Y)

更新后的图片(170% Y)

来源:

#define CLAMP(val) {val = (val > 255) ? 255 : ((val < 0) ? 0 : val);}

void Brighten(Gdiplus::BitmapData& dataIn, Gdiplus::BitmapData& dataOut, const double YMultiplier=1.3)
{
    if ( ((dataIn.PixelFormat != PixelFormat24bppRGB) && (dataIn.PixelFormat != PixelFormat32bppARGB)) ||
         ((dataOut.PixelFormat != PixelFormat24bppRGB) && (dataOut.PixelFormat != PixelFormat32bppARGB)))
    {
        return;
    }

    if ((dataIn.Width != dataOut.Width) || (dataIn.Height != dataOut.Height))
    {
        // images sizes aren't the same
        return;
    }


    const size_t incrementIn = dataIn.PixelFormat == PixelFormat24bppRGB ? 3 : 4;
    const size_t incrementOut = dataIn.PixelFormat == PixelFormat24bppRGB ? 3 : 4;
    const size_t width = dataIn.Width;
    const size_t height = dataIn.Height;


    for (size_t y = 0; y < height; y++)
    {
        auto ptrRowIn = (BYTE*)(dataIn.Scan0) + (y * dataIn.Stride);
        auto ptrRowOut = (BYTE*)(dataOut.Scan0) + (y * dataOut.Stride);

        for (size_t x = 0; x < width; x++)
        {
            uint8_t B = ptrRowIn[0];
            uint8_t G = ptrRowIn[1];
            uint8_t R = ptrRowIn[2];
            uint8_t A = (incrementIn == 3) ? 0xFF : ptrRowIn[3];

            auto Y = (0.257 * R) + (0.504 * G) + (0.098 * B) + 16;
            auto V = (0.439 * R) - (0.368 * G) - (0.071 * B) + 128;
            auto U = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128;

            Y *= YMultiplier;

            auto newB = 1.164*(Y - 16) + 2.018*(U - 128);
            auto newG = 1.164*(Y - 16) - 0.813*(V - 128) - 0.391*(U - 128);
            auto newR = 1.164*(Y - 16) + 1.596*(V - 128);

            CLAMP(newR);
            CLAMP(newG);
            CLAMP(newB);

            ptrRowOut[0] = newB;
            ptrRowOut[1] = newG;
            ptrRowOut[2] = newR;
            if (incrementOut == 4)
            {
                ptrRowOut[3] = A; // keep original alpha
            }

            ptrRowIn += incrementIn;
            ptrRowOut += incrementOut;
        }
    }
}

【讨论】:

  • 对不起,它根本不起作用。保存后像素显示为黑色。亮度值为 250。
  • 你能显示你的代码吗?我之前写过很多 YUV 转换器,我相信我可以发现任何问题和/或纠正。
  • 这些公式可能期望 R、G、B 处于不同的标准化范围内。检查。
  • 在我可以进一步研究他之前,我将提出这个答案。很抱歉造成混乱。
  • @PacCol - 我已经确认该算法有效,并通过一些示例转换更新了我的答案。如果您得到黑色像素,请确保您考虑了诸如 alpha 通道、字节顺序(ARGB 与 BGRA、步幅等)等因素。
猜你喜欢
  • 1970-01-01
  • 2019-04-13
  • 2019-03-08
  • 1970-01-01
  • 2020-07-20
  • 1970-01-01
  • 1970-01-01
  • 2021-10-25
  • 2011-11-18
相关资源
最近更新 更多