Преобразование стандартного RGB в оттенки серого

Я пытаюсь написать алгоритм преобразователя, который берет изображение JPEG и возвращает его версию PGM (Portable Gray Map). Проблема в том, что я не могу понять, как работают «официальные» конвертеры JPG-> PGM с точки зрения того, какое значение присваивать конечному пикселю (я думаю, 0-> 255), начиная с классического формата RGB.

Вначале я использовал эту формулу (она та же, что используется при преобразовании CV_RGB2GRAY OpenCV):

0,30 * R + 0,59 * G + 0,11 * B = значение

Я написал простой код для проверки своих результатов: он берет цветное изображение и его версию PGM (уже преобразованную с помощью GIMP). Затем он преобразует цветное изображение по предыдущей формуле. Цель состоит в том, чтобы получить изображение в градациях серого, которое попиксельно равно входному PGM.

На данный момент он не возвращает те же значения. Можешь мне помочь?


person TheUnexpected    schedule 12.07.2013    source источник
comment
Вы уже знакомы с wikipedia: Grayscale?   -  person MrSmith42    schedule 12.07.2013
comment
Вы уверены, что это то, что он делает? Что, если он просто декодирует плоскость Y и игнорирует цветовые коэффициенты? У вас будет другой шум, и факторы могут быть разными.   -  person harold    schedule 12.07.2013
comment
Извините, я не понимаю ваш пост   -  person TheUnexpected    schedule 12.07.2013


Ответы (6)


Проблема в том, что я не могу понять, как работают «официальные» конвертеры JPG-> PGM с точки зрения того, какое значение присваивать конечному пикселю (я думаю, 0-> 255), начиная с классического формата RGB.

Вероятно, существует корректировка гаммы в преобразовании, которое используют эти "официальные" инструменты.
То есть это не просто линейное преобразование.

Подробнее см. В этом разделе Википедии: Преобразование цвета в оттенки серого

Я считаю, что вы хотите использовать формулу для Csrgb.
Попробуйте и посмотрите, соответствует ли она ожидаемым вами результатам.

По сути, вы сделаете это:

  1. Take R, G, B color (each in [0,1] range)
    • If they're in the range 0..255 instead, simply divide by 255.0
  2. Compute Clinear = 0.2126 R + 0.7152 G + 0.0722 B
    • This is likely the linear transform you were applying before
  3. Compute Csrgb according to it's formula, based on Clinear
    • This is the nonlinear gamma correction piece you were missing
    • Проверьте этот график WolframAlpha
    • Csrgb = 12.92 Clinear когда Clinear <= 0.0031308
    • Csrgb = 1.055 Clinear1/2.4 - 0.055 когда Clinear > 0.0031308
person Timothy Shields    schedule 12.07.2013
comment
@ alessandro.francesconi Я немного обновил ответ, чтобы изложить точные шаги, поскольку страница Википедии может быть немного загадочной, если вы не знакомы с некоторыми основами науки о цвете. - person Timothy Shields; 12.07.2013
comment
@ alessandro.francesconi Я также добавил для вас график WolframAlpha, чтобы вы могли видеть нелинейную форму гамма-коррекции. - person Timothy Shields; 12.07.2013
comment
Тимоти, пожалуйста, поправьте меня, если я ошибаюсь, но я думаю, что после шага (1) вам нужно преобразовать значения в линейную интенсивность, поскольку, когда вы берете значения RGB из файла, они уже имеют гамма-кодировку с мощностью 1 / 2,4. Итак, сначала вам нужно удалить эту кодировку, применив преобразование с мощностью 2.4, и только затем выполнить шаги (2) и (3) вашего ответа. Это правильно? - person John Smith; 24.01.2018
comment
@JohnSmith Вы правы. Его расширяют, находят линейную яркость, затем сжимают. - person James South; 11.09.2018

По мнению Гарольда о "плоскости Y": стандартные цветные JPEG-файлы кодируются с использованием цветового пространства YCbCr, где Y - составляющая яркости (т. е. яркость), а Cb и Cr - составляющие цветности синего и красного цветов. Итак, один из способов превратить цветной JPEG в полутоновый - просто отбросить компоненты Cb и Cr.

Существует утилита под названием jpegtran, которая может делать это без потерь, используя параметр -grayscale. (Часть без потерь будет иметь значение только в том случае, если вы хотите получить JPEG, а не PGM, чтобы избежать генерации потеря.) В любом случае, это, вероятно, был бы самым быстрым способом сделать это преобразование, потому что он даже не декодирует изображение в пиксели, не говоря уже о математических вычислениях для каждого из них.

person Robert Fleming    schedule 15.12.2017

Теоретически, имея несколько пикселей (в данном случае 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.

Затем решите эту проблему (найдите «решатель уравнений» или что-то в этом роде) и посмотрите, какие константы они используют.

person Fabinout    schedule 12.07.2013
comment
Спасибо, но нет, это не работает. Я поставил 3 значения пикселя и решил систему из трех уравнений. Это приводит к трем константам, которые подходят для этих уравнений, а не для четвертого пикселя. - person TheUnexpected; 12.07.2013
comment
1) Вы уверены, что выбрали одни и те же пиксели для значений RGB и серого? 2) из ​​этой статьи: tannerhelland.com/3643/grayscale-image-algorithm -vb6 Я видел, что это были несколько разных алгоритмов преобразования RGB в PGM. Попробуйте их все и попытайтесь определить, какой из них используется. Удачи! - person Fabinout; 12.07.2013
comment
Что, если я скажу вам, что не нашел подходящего метода? - person TheUnexpected; 12.07.2013
comment
Что ж, GIMP, вероятно, использует свой собственный странный алгоритм, какова реальная цель попытки воссоздать именно их конвертер? - person Fabinout; 12.07.2013
comment
Процесс преобразования PGM - это часть более длинного алгоритма. После некоторых тестов я увидел, что результаты такого алгоритма будут лучше, если я буду использовать в качестве входных данных изображение GIMP PGM, а не более простую версию, созданную с помощью всех этих методов. Итак, я подумал, что настоящий формат PGM описывает значения пикселей таким образом, который кажется более ... управляемым моим алгоритмом. Я также пробовал посмотреть код GIMP, я нашел возможную точку преобразования, но он не так удобочитаем ... - person TheUnexpected; 12.07.2013
comment
@Fabinout Дело не в том, что GIMP [использует] свой собственный странный алгоритм. Это гамма-коррекция. См. Мой ответ и страницу в Википедии, на которую он ссылается. - person Timothy Shields; 12.07.2013

ПРОСТОЙ АЛГОРИТМ ПРЕОБРАЗОВАНИЯ ИЗОБРАЖЕНИЯ RGB В СЕРЫЙ В OPENCV PYTHON!

Я использовал комментарии, поэтому код не требует пояснений, но работает быстро.

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

Я использовал этот файл изображения ...  введите описание изображения здесь

Моя программа сгенерировала следующий файл Grayscale ..

введите описание изображения здесь

person bad programmer    schedule 20.05.2017
comment
Итерация по всем пикселям в коде Python, выполнение вычислений в Python и добавление результата в список только для преобразования его обратно в массив numpy будет чертовски медленным (и использует примерно в 4-8 раз больше памяти, чем оптимальное решение ). Поскольку у вас уже есть numpy, вы можете выполнить все вычисления на стороне C, используя широковещательную рассылку, намного быстрее и избегая траты памяти. - person Matteo Italia; 28.06.2018
comment
Итак, после загрузки изображения все это можно уменьшить до gr = img1[:,:,0]*0.07 + img1[:,:,1]*0.72 + img1[:,:,2]*0.21 cv2.imwrite("test1.png", gr). На моей машине ваш код с вашим изображением занимает 0,41 секунды, мой код 0,06; разница более значительна с большими изображениями. - person Matteo Italia; 28.06.2018
comment
имя пользователя проверено - person Plagon; 21.11.2019

Преобразует один входной пиксель в 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);
}
person Tamara Koliada    schedule 02.01.2019

Метод среднего - самый простой. Вам просто нужно взять среднее значение трех цветов. Поскольку это изображение RGB, это означает, что вы добавили r, g и b, а затем разделили его на 3, чтобы получить желаемое изображение в оттенках серого.

Это делается вот так.

Grayscale = (R + G + B / 3)

Если у вас есть цветное изображение, подобное изображенному выше, и вы хотите преобразовать его в оттенки серого, используя средний метод.

person Community    schedule 02.02.2019
comment
Это даже не делит сумму на 3 правильно, так как компонент B делится только на три. Кроме того, брать среднее значение неверно. - person keith; 12.04.2020
comment
Это неправильно, круглые скобки расположены неправильно и не позволяют получить правильный порядок операций для среднего ... и хотя это один метод, он не подходит для правильной шкалы серого, поскольку глаза по-разному реагируют на каждый из трех цветов ... правая формула: оттенки серого = 0,299 * R + 0,587 * G + 0,114 * B - person Dan Ortega; 01.02.2021