"Стандартное" преобразование RGB в оттенки серого

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

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

0,30 * R + 0,59 * G + 0,11 * B = val

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

В этот момент он не возвращает те же значения. Вы можете мне помочь?

Ответ 1

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

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

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

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

В принципе, вы сделаете это:

  • Возьмите цвет R, G, B (каждый в диапазоне [0,1])
    • Если они находятся в диапазоне 0..255 вместо этого, просто разделите на 255.0
  • Вычислить Clinear = 0.2126 R + 0.7152 G + 0.0722 B
    • Скорее всего, это линейное преобразование, которое вы применяли до
  • Вычислить Csrgb согласно его формуле на основе Clinear
    • Это нелинейная гамма-коррекция, которую вы отсутствовали
    • Отметьте этот график WolframAlpha
    • Csrgb = 12.92 Clinear, когда Clinear <= 0.0031308
    • Csrgb = 1.055 Clinear1/2.4 - 0.055, когда Clinear > 0.0031308

Ответ 2

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

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

Ответ 3

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

Это сделано таким образом.

Grayscale = (R + G + B / 3)

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

Ответ 4

В теории, с несколькими пикселями (3, в данном случае), вы можете определить, что делает их алгоритм. Juste выбрать три пикселя (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.

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

Ответ 5

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

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

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

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

Моя программа, созданная после файла Grayscale.

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

Ответ 6

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