Как я могу преобразовать изображение RGB в оттенки серого в Python?

206

Я пытаюсь использовать matplotlib чтобы прочитать изображение RGB и преобразовать его в оттенки серого.

В Matlab я использую это:

img = rgb2gray(imread('image.png'));

В уроке matplotlib они не освещают это. Они просто читают на картинке

import matplotlib.image as mpimg
img = mpimg.imread('image.png')

и затем они нарезают массив, но это не то же самое, что преобразование RGB в оттенки серого из того, что я понимаю.

lum_img = img[:,:,0]

Мне трудно поверить, что у numpy или matplotlib нет встроенной функции для преобразования из rgb в серый. Разве это не обычная операция при обработке изображений?

Я написал очень простую функцию, которая работает с импортированным изображением imreadза 5 минут. Это ужасно неэффективно, но именно поэтому я надеялся на встроенную профессиональную реализацию.

Себастьян улучшил мою функцию, но я все еще надеюсь найти встроенную.

Реализация Matlab (NTSC / PAL):

import numpy as np

def rgb2gray(rgb):

    r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
    gray = 0.2989 * r + 0.5870 * g + 0.1140 * b

    return gray
Оспинатор
источник
2
Обратите внимание , что вы можете написать то же самое , как ваш rgb2gray функции просто как: gray = np.mean(rgb, -1). Может быть, rgb[...,:3]если это на самом деле RGBA.
Себерг
хм, gray = np.mean(rgb, -1)отлично работает. Спасибо. Есть ли причина не использовать это? Почему бы вместо этого использовать решения в ответах ниже?
оспинатор
6
На странице Википедии в градациях серого говорится, что метод преобразования RGB в градации серого не является уникальным, но в нем приводятся часто используемые формулы, основанные на яркости. Это совсем не то, что np.mean(rgb, -1).
unutbu
2
так что я думаю, что я хочу версию Matlab ? 0.2989 * R + 0.5870 * G + 0.1140 * B Я предполагаю, что это стандартный способ сделать это.
оспинатор

Ответы:

304

Как насчет того, чтобы сделать это с подушкой :

from PIL import Image
img = Image.open('image.png').convert('LA')
img.save('greyscale.png')

Используя matplotlib и формулу

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

Вы могли бы сделать:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

def rgb2gray(rgb):
    return np.dot(rgb[...,:3], [0.2989, 0.5870, 0.1140])

img = mpimg.imread('image.png')     
gray = rgb2gray(img)    
plt.imshow(gray, cmap=plt.get_cmap('gray'), vmin=0, vmax=1)
plt.show()
unutbu
источник
3
Если он должен использовать matplotlibпо какой-то другой причине, он должен быть в состоянии использовать встроенную colorsys.rgb_to_yiq()функцию для преобразования плюс часть, чтобы получить только канал яркости.
Сайлас Рэй
35
почему .convert('LA')? почему нет .convert('gray')? Кажется излишне загадочным. В документации PIL ничего не говорится о «LA» для функции convert.
оспинатор
25
используя PIL:, cannot write mode LA as JPEGмне нужно было использовать режим L, а не LA
jsky
6
Это img = Image.open('image.png').convert('LA')должно бытьimg = Image.open('image.png').convert('L')
nviens
12
@BluePython: LAрежим имеет яркость (яркость) и альфа. Если вы используете LAрежим, то greyscale.pngбудет изображение RGBA с сохраненным альфа-каналом image.png. Если вы используете Lрежим, то greyscale.pngбудет изображение RGB (без альфа).
unutbu
69

Вы также можете использовать scikit-image , который предоставляет некоторые функции для преобразования изображения ndarray, например rgb2gray.

from skimage import color
from skimage import io

img = color.rgb2gray(io.imread('image.png'))

Ноты : веса, использованные в этом преобразовании, откалиброваны для современных люминофорных ЭЛТ: Y = 0,2125 R + 0,7154 G + 0,0721 B

Кроме того, вы можете прочитать изображение в оттенках серого с помощью:

from skimage import io
img = io.imread('image.png', as_gray=True)
yangjie
источник
это нормально, что я получаю 0 <значения <1? Я должен умножить их на 255, чтобы получить настоящую серую шкалу?
Сэм
зная, что моя цель - использовать возможности GLCM (greycoprops)
Сэм
Примечание для io.imread: "as_grey" устарела в пользу "as_gray". То же использование, только американизированное написание. :)
Галоген
1
Я считаю, что это самый полезный ответ на поставленный вопрос, вывод этого также совместим с matplotlib и numpy.
Мерт Бешиктепе
Я использую цветной объект, но мое изображение теперь красноватое, а не серое (черно-белое). Мне нужно использовать cmapкак gray' then only the image is shown as gray in pyplot.imshow () `? Есть предположения ? Где я не прав?
GadaaDhaariGeek
63

Три из предложенных методов были протестированы на скорость с 1000 PNG-изображениями RGBA (224 x 256 пикселей), работающими с Python 3.5 на Ubuntu 16.04 LTS (Xeon E5 2670 с SSD).

Среднее время выполнения

pil : 1,037 секунды

scipy: 1,040 секунды

sk : 2.120 секунд

PIL и SciPy дали одинаковые numpyмассивы (от 0 до 255). SkImage дает массивы от 0 до 1. Кроме того, цвета немного преобразуются, см. Пример из набора данных CUB-200.

SkImage: SkImage

PIL : PIL

SciPy : SciPy

Original: оригинал

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

Код

  1. Производительность

    run_times = dict(sk=list(), pil=list(), scipy=list())
    for t in range(100):
        start_time = time.time()
        for i in range(1000):
            z = random.choice(filenames_png)
            img = skimage.color.rgb2gray(skimage.io.imread(z))
        run_times['sk'].append(time.time() - start_time)

    start_time = time.time()
    for i in range(1000):
        z = random.choice(filenames_png)
        img = np.array(Image.open(z).convert('L'))
    run_times['pil'].append(time.time() - start_time)
    
    start_time = time.time()
    for i in range(1000):
        z = random.choice(filenames_png)
        img = scipy.ndimage.imread(z, mode='L')
    run_times['scipy'].append(time.time() - start_time)
    

    for k, v in run_times.items(): print('{:5}: {:0.3f} seconds'.format(k, sum(v) / len(v)))

  2. Вывод
    z = 'Cardinal_0007_3025810472.jpg'
    img1 = skimage.color.rgb2gray(skimage.io.imread(z)) * 255
    IPython.display.display(PIL.Image.fromarray(img1).convert('RGB'))
    img2 = np.array(Image.open(z).convert('L'))
    IPython.display.display(PIL.Image.fromarray(img2))
    img3 = scipy.ndimage.imread(z, mode='L')
    IPython.display.display(PIL.Image.fromarray(img3))
  3. сравнение
    img_diff = np.ndarray(shape=img1.shape, dtype='float32')
    img_diff.fill(128)
    img_diff += (img1 - img3)
    img_diff -= img_diff.min()
    img_diff *= (255/img_diff.max())
    IPython.display.display(PIL.Image.fromarray(img_diff).convert('RGB'))
  4. импорт
    import skimage.color
    import skimage.io
    import random
    import time
    from PIL import Image
    import numpy as np
    import scipy.ndimage
    import IPython.display
  5. Версии
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1
Максимилиан Петерс
источник
6
Изображение ввода / вывода SciPy буквально PIL / Pillow. Следовательно, тестирование SciPy - это эффективное повторное тестирование PIL / Pillow с незначительными накладными расходами, вызванными функциями оболочки SciPy. Было бы гораздо полезнее заменить OpenCV (который не использует PIL / Pillow) на SciPy (который делает). Тем не менее, спасибо за выделенный бенчмаркинг! Заметное замедление, навязанное SciKit, является захватывающим ... и ужасающим.
Сесил Карри
@CecilCurry Спасибо за идею с OpenCV! Я добавлю это, когда найду свободное время.
Максимилиан Питерс
Upvoted! Не тот ответ, который я искал, но тем не менее, очень и очень интересный :)
Кирилл Н.
29

Вы всегда можете прочитать файл изображения в градациях серого с самого начала, используя imreadOpenCV:

img = cv2.imread('messi5.jpg', 0)

Кроме того, если вы хотите прочитать изображение как RGB, выполните некоторую обработку, а затем конвертируйте его в шкалу серого, которую вы можете использовать cvtcolorиз OpenCV:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
Диамантатос Параскевас
источник
6
Ftr: 0флаг есть cv2.CV_LOAD_IMAGE_GRAYSCALE.
ДТК
24

Самый быстрый и актуальный способ - использовать подушку , установленную черезpip install Pillow .

Код тогда:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')
YPCrumble
источник
3
обратите внимание, что если вы не объединяете свои методы, как в примере выше, convertвозвращает преобразованную копию изображения
Matt
не работает на 32 - битном формате PNG, значения будут зажаты до 255
Эндрю Matuk
11

Учебник обманывает, потому что он начинается с изображения в градациях серого, закодированного в RGB, поэтому они просто обрезают один цветной канал и рассматривают его как оттенки серого. Основные шаги, которые вам нужно сделать, - это преобразовать цветовое пространство RGB в цветовое пространство, которое кодирует с помощью чего-то, приближающего модель яркости / цветности, например, YUV / YIQ или HSL / HSV, затем отрезать канал, подобный яркости, и использовать его как ваше изображение в градациях серого. matplotlibне предоставляет механизм для преобразования в YUV / YIQ, но он позволяет преобразовать в HSV.

Попробуйте использовать matplotlib.colors.rgb_to_hsv(img)затем вырезание последнего значения (V) из массива для градаций серого. Это не совсем то же самое, что значение яркости, но это означает, что вы можете сделать все это в matplotlib.

Задний план:

Кроме того, вы можете использовать PIL или встроенную colorsys.rgb_to_yiq()функцию для преобразования в цветовое пространство с истинным значением яркости. Вы также можете пойти ва-банк и выпустить свой собственный конвертер только для яркости, хотя это, вероятно, излишне.

Сайлас Рэй
источник
9

Используя эту формулу

Y' = 0.299 R + 0.587 G + 0.114 B 

Мы можем сделать

import imageio
import numpy as np
import matplotlib.pyplot as plt

pic = imageio.imread('(image)')
gray = lambda rgb : np.dot(rgb[... , :3] , [0.299 , 0.587, 0.114]) 
gray = gray(pic)  
plt.imshow(gray, cmap = plt.get_cmap(name = 'gray'))

Тем не менее, программа GIMP, преобразующая цвета в изображения в градациях серого, имеет три алгоритма для выполнения этой задачи.

интернет ресурсов Азербайджана
источник
8

Если вы уже используете NumPy / SciPy, вы также можете использовать :

scipy.ndimage.imread(file_name, mode='L')

ДТК
источник
5
Оба , scipy.ndimage.imread()и scipy.misc.imread()являются официально осуждается в SciPy 1.0.0 и будут удалены в SciPy 1.2.0. В то время как документация SciPy рекомендует imageio.imread()в качестве подходящей замены, API этой функции - просто до абсурда. Он не поддерживает преобразование в градациях серого и, таким образом, остается непригодным для многих приложений, включая наши. </sigh>
Сесил Карри
5
@CecilCurry, как конвертировать цветное изображение в оттенки серого с помощью imageio?
0x90
5

Вы могли бы сделать:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

def rgb_to_gray(img):
        grayImage = np.zeros(img.shape)
        R = np.array(img[:, :, 0])
        G = np.array(img[:, :, 1])
        B = np.array(img[:, :, 2])

        R = (R *.299)
        G = (G *.587)
        B = (B *.114)

        Avg = (R+G+B)
        grayImage = img

        for i in range(3):
           grayImage[:,:,i] = Avg

        return grayImage       

image = mpimg.imread("your_image.png")   
grayImage = rgb_to_gray(image)  
plt.imshow(grayImage)
plt.show()
am.mansour
источник
5

Используйте img.Convert (), поддерживает «L», «RGB» и «CMYK». Режим

import numpy as np
from PIL import Image

img = Image.open("IMG/center_2018_02_03_00_34_32_784.jpg")
img.convert('L')

print np.array(img)

Вывод:

[[135 123 134 ...,  30   3  14]
 [137 130 137 ...,   9  20  13]
 [170 177 183 ...,  14  10 250]
 ..., 
 [112  99  91 ...,  90  88  80]
 [ 95 103 111 ..., 102  85 103]
 [112  96  86 ..., 182 148 114]]
Naren
источник
1
должна быть 5-я строка img = img.convert('L')?
Аллан Руин
3

Я пришел к этому вопросу через Google, ища способ конвертировать уже загруженное изображение в оттенки серого.

Вот способ сделать это с помощью SciPy:

import scipy.misc
import scipy.ndimage

# Load an example image
# Use scipy.ndimage.imread(file_name, mode='L') if you have your own
img = scipy.misc.face()

# Convert the image
R = img[:, :, 0]
G = img[:, :, 1]
B = img[:, :, 2]
img_gray = R * 299. / 1000 + G * 587. / 1000 + B * 114. / 1000

# Show the image
scipy.misc.imshow(img_gray)
Мартин Тома
источник
1
Ницца. Я просто хочу отметить, что более короткое решение было быimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall
@Akavall Приятно знать, спасибо! Знаете ли вы, если ваш ярлык быстрее? Если нет, я бы оставил свой, потому что это легче понять.
Мартин Тома,
Я не успел это сделать, мое внутреннее чувство numpy.averageнемного быстрее, но практически не отличается. Ваше решение ясное и содержит соответствующую информацию о R, G, B, поэтому я бы его сохранил. Мой комментарий был скорее дополнительной опцией, а не заменой.
Akavall
Оба , scipy.ndimage.imread()и scipy.misc.imread()являются официально осуждается в SciPy 1.0.0 и будут удалены в SciPy 1.2.0. Вы , наверное , просто хотите использовать поддержку преобразования встроенных полутоновую подушки ( в ала unutbu «s ответ ), вместо этого.
Сесил Карри
-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

Вы можете использовать greyscale()непосредственно для преобразования.

Цянь Хань
источник