【问题标题】:"Standard" RGB to Grayscale Conversion“标准”RGB 到灰度转换
【发布时间】:2013-07-11 01:23:39
【问题描述】:

我正在尝试编写一个转换器算法,该算法采用 JPEG 图像并返回其 PGM(便携式灰度图)版本。 问题是我无法理解“官方”JPG->PGM 转换器如何从经典 RGB 格式开始分配给最终像素(我猜是 0->255)的值。

一开始我用的是这个公式(和OpenCV的CV_RGB2GRAY转换用的一样):

0.30*R + 0.59*G + 0.11*B = 值

我编写了一个简单的代码来测试我的结果:它采用彩色图像及其 PGM 版本(已使用 GIMP 转换)。然后它使用前面的公式转换彩色图像。目标是得到一个像素到像素等于 PGM 输入的灰度图像。

此时,它不会返回相同的值。你能帮助我吗?

【问题讨论】:

  • 你已经看过wikipedia: Grayscale了吗?
  • 你确定它是这样做的吗?如果它只是解码 Y 平面并忽略颜色系数怎么办?你会有不同的噪音,而且因素可能不同。
  • 对不起,我不明白你的帖子

标签: image algorithm jpeg file-conversion pgm


【解决方案1】:

问题是我无法理解“官方”JPG->PGM 转换器如何从经典的 RGB 格式开始分配给最终像素(我猜是 0->255)的值。

那些“官方”工具正在使用的转换中可能存在伽玛调整。
也就是说,它不仅仅是线性变换。

有关详细信息,请参阅此 Wikipedia 部分:Converting color to grayscale

我相信您想使用C<sub>srgb</sub> 的公式。
试试看它是否符合您的预期。

基本上,你会这样做:

  1. R, G, B颜色(每个在[0,1]范围内)
    • 如果它们在0..255 范围内,只需除以255.0
  2. 计算C<sub>linear</sub> = 0.2126 R + 0.7152 G + 0.0722 B
    • 这可能是您之前应用的线性变换
  3. 根据公式计算C<sub>srgb</sub>,基于C<sub>linear</sub>
    • 这是您缺少的非线性伽马校正部分
    • 查看this WolframAlpha plot
    • C<sub>srgb</sub> = 12.92 C<sub>linear</sub>C<sub>linear</sub> &lt;= 0.0031308
    • C<sub>srgb</sub> = 1.055 C<sub>linear</sub><sup>1/2.4</sup> - 0.055C<sub>linear</sub> &gt; 0.0031308

【讨论】:

  • @alessandro.francesconi 我稍微更新了答案以阐明确切的步骤,因为如果您不熟悉一些基本的色彩科学,维基百科页面可能有点神秘。
  • @alessandro.francesconi 我还为您添加了 WolframAlpha 图,以便您可以看到伽马校正的非线性形状。
  • 蒂莫西,如果我错了,请纠正我,但我认为在步骤 (1) 之后,您必须将值转换为线性强度,因为当您从文件中获取 RGB 值时,它们已经是伽马编码的功率为 1/2.4。因此,首先您需要通过应用具有 2.4 次幂的变换来删除此编码,然后才执行答案的步骤 (2) 和 (3)。对吗?
  • @JohnSmith 你是对的。它是展开,找到线性亮度,然后压缩。
【解决方案2】:

哈罗德关于“Y 平面”的观点:标准彩色 JPEG 使用YCbCr 颜色空间进行编码,其中 Y 是亮度分量(即亮度),Cb 和 Cr 是蓝差和红差色度成分。因此,将彩色 JPEG 转换为灰度的一种方法是简单地删除 Cb 和 Cr 分量。

有一个名为jpegtran 的实用程序可以使用-grayscale 选项无损地执行此操作。 (无损部分只有在你想以 JPEG 而不是 PGM 结束时才有意义,以避免generation loss。)无论如何,这可能是进行这种转换的最快方法,因为它甚至没有将图像解码为像素,更不用说对每个像素进行数学运算了。

【讨论】:

    【解决方案3】:

    理论上,只需几个像素(在本例中为 3 个),您就可以确定他们的算法在做什么。 只要选择你的三个像素(p1、p2、p3),它们的 RGB 值和它们的 PGM 灰度值,你就有了:

    RedConstant * p1.redValue + GreenConstant * p1.greenValue + BlueConstant * p1.blueValue = p1.grayValue

    RedConstant * p2.redValue + GreenConstant * p2.greenValue + BlueConstant * p2.blueValue = p2.grayValue

    RedConstant * p3.redValue + GreenConstant * p3.greenValue + BlueConstant * p3.blueValue = p3.grayValue。

    然后解决这个问题(查找“方程求解器”或其他东西),看看他们使用的常量是什么。

    【讨论】:

    • 谢谢,但是,不,它不起作用。我输入了 3 个像素值并求解了一个三方程系统。它产生了三个常数,这些常数对这些方程有利,但对第四个像素不利。
    • 1) 你确定你为 RGB 和灰度值选择了相同的像素吗? 2)从这篇文章:tannerhelland.com/3643/grayscale-image-algorithm-vb6我看到它们是几种不同的RGB-to-PGM算法。全部尝试并尝试发现使用了哪一个。祝你好运!
    • 如果我告诉你我没有找到任何有效的方法怎么办?
    • 好吧,GIMP 可能使用了他们自己的奇怪算法,尝试重新创建他们的转换器的真正目的是什么?
    • PGM 转换过程是更长算法的子部分。经过一些测试,我发现如果我使用“GIMP”PGM 图像作为输入,而不是使用所有这些方法创建的更简单的版本,这种算法的结果会更好。所以我认为真正的 PGM 格式描述像素值的方式似乎更...“可管理”我的算法。我也尝试过查看 GIMP 的代码,我发现了一个可能的转换点,但它的可读性不高……
    【解决方案4】:

    在 OPENCV PYTHON 中将 RGB 图像转换为灰度的简单算法!

    我使用了 cmets,所以代码是不言自明的。但它运行迅速。

    import cv2
    import numpy as np
    img1 = cv2.imread('opencvlogo.png')
    row,col,ch = img1.shape
    g = [ ]  #the list in which we will stuff single grayscale pixel value inplace of 3 RBG values
    #this function converts each RGB pixel value into single Grayscale pixel value and appends that value to list 'g'
    def rgb2gray(Img):
        global g
        row,col,CHANNEL = Img.shape
        for i in range(row) :
            for j in range(col):
            a =      (   Img[i,j,0]*0.07  +  Img[i,j,1]*0.72 +    Img[i,j,2] *0.21   ) #the algorithm i used id , G =  B*0.07 + G*0.72 + R* 0.21
                                                                                       #I found it online
            g.append(a)
    rgb2gray(img1)  #convert the img1 into grayscale
    gr = np.array(g)  #convert the list 'g' containing grayscale pixel values into numpy array
    cv2.imwrite("test1.png" , gr.reshape(row,col)) #save the image file as test1.jpg
    

    所以我使用了这个图像文件...

    我的程序生成以下灰度文件..

    【讨论】:

    • 迭代 Python 代码中的所有像素,在 Python 中执行计算并将结果附加到列表中,只是为了将其转换回 numpy 数组,这将非常慢(并且使用大约 4 到内存是最优解的 8 倍)。由于您已经拥有 numpy,您可以使用广播在 C 端执行整个计算,这样更快并且避免浪费内存。
    • 所以,加载完图片后,整个东西就可以缩减为gr = img1[:,:,0]*0.07 + img1[:,:,1]*0.72 + img1[:,:,2]*0.21cv2.imwrite("test1.png", gr)。在我的机器上,您的图像代码需要 0.41 秒,我的代码是 0.06;图像越大,差异越大。
    • 用户名签出
    【解决方案5】:

    将默认 RGB ColorModel 中的单个输入像素转换为单个灰色像素。

    /* Convertation function 
     * @param x    the horizontal pixel coordinate
     * @param y    the vertical pixel coordinate
     * @param rgb  the integer pixel representation in the default RGB color model
     * @return a gray pixel in the default RGB color model.*/
    
        public int filterRGB(int x, int y, int rgb) {
        // Find the average of red, green, and blue.
        float avg = (((rgb >> 16) & 0xff) / 255f +
                     ((rgb >>  8) & 0xff) / 255f +
                      (rgb        & 0xff) / 255f) / 3;
        // Pull out the alpha channel.
        float alpha = (((rgb >> 24) & 0xff) / 255f);
    
        // Calculate the average.
        // Formula: Math.min(1.0f, (1f - avg) / (100.0f / 35.0f) + avg);
        // The following formula uses less operations and hence is faster.
        avg = Math.min(1.0f, 0.35f + 0.65f * avg);
        // Convert back into RGB.
       return (int) (alpha * 255f) << 24 |
              (int) (avg   * 255f) << 16 |
              (int) (avg   * 255f) << 8  |
              (int) (avg   * 255f);
    }
    

    【讨论】:

      【解决方案6】:

      平均法是最简单的一种。你只需要取三种颜色的平均值。由于它是 RGB 图像,所以这意味着您已将 r 与 g 与 b 相加,然后将其除以 3 以获得所需的灰度图像。

      这样就完成了。

      Grayscale = (R + G + B / 3)
      

      如果您有如上图所示的彩色图像,并且您想使用平均法将其转换为灰度。

      【讨论】:

      • 这甚至不能正确地将总和除以 3,因为它只将 B 分量除以 3。此外,取平均值也不正确。
      • 这是错误的,括号位置错误并且没有得到正确的平均操作顺序......虽然这是一种方法,但不适用于正确的灰度,因为眼睛对三种颜色中的每一种..正确的公式是灰度 = 0.299 * R + 0.587 * G + 0.114 * B
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      • 2013-09-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多