Làm cách nào tôi có thể chuyển đổi hình ảnh RGB thành thang độ xám trong Python?


205

Tôi đang cố gắng sử dụng matplotlibđể đọc trong hình ảnh RGB và chuyển đổi nó thành thang độ xám.

Trong MATLAB tôi sử dụng điều này:

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

Trong hướng dẫn matplotlib, họ không bao gồm nó. Họ chỉ đọc trong hình

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

và sau đó họ cắt mảng, nhưng đó không giống như chuyển đổi RGB sang thang độ xám từ những gì tôi hiểu.

lum_img = img[:,:,0]

Tôi cảm thấy khó tin rằng numpy hoặc matplotlib không có chức năng tích hợp để chuyển đổi từ rgb sang màu xám. Đây không phải là một hoạt động phổ biến trong xử lý hình ảnh?

Tôi đã viết một chức năng rất đơn giản, hoạt động với hình ảnh được nhập imreadtrong 5 phút. Đó là không hiệu quả khủng khiếp, nhưng đó là lý do tại sao tôi hy vọng cho một triển khai chuyên nghiệp tích hợp.

Sebastian đã cải thiện chức năng của tôi, nhưng tôi vẫn hy vọng tìm thấy một chức năng tích hợp.

triển khai 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
Lưu ý rằng bạn có thể viết điều tương tự như chức năng rgb2gray của bạn chỉ đơn giản là : gray = np.mean(rgb, -1). Có lẽ rgb[...,:3]ở đó nếu nó thực sự là rgba.
seberg

hmm, gray = np.mean(rgb, -1)hoạt động tốt cảm ơn. Có bất kỳ lý do để không sử dụng này? Tại sao tôi lại sử dụng các giải pháp trong các câu trả lời dưới đây?
đóng băng

6
Các màu xám wikipedia trang cho biết phương pháp chuyển đổi RGB sang gam màu xám không phải là duy nhất, nhưng đưa ra một công thức thường được sử dụng dựa trên độ sáng. Nó là khá khác nhau hơn np.mean(rgb, -1).
unutbu

2
vì vậy tôi đoán tôi muốn phiên bản của Matlab ? 0.2989 * R + 0.5870 * G + 0.1140 * B Tôi cho rằng đó là cách làm tiêu chuẩn.
đo tốc độ

Câu trả lời:


303

Làm thế nào với nó với Gối :

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

Sử dụng matplotlib và công thức

Y' = 0.2989 R + 0.5870 G + 0.1140 B 

bạn có thể làm:

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()

3
Nếu anh ta phải sử dụng matplotlibvì một số lý do khác, anh ta có thể sử dụng nội dung dựng sẵn colorsys.rgb_to_yiq()để biến đổi cộng với một lát cắt để chỉ lấy kênh luma.
Silas Ray

34
tại sao .convert('LA')? tại sao không .convert('gray')? Có vẻ khó hiểu. Các tài liệu PIL không đề cập đến bất cứ điều gì về 'LA' cho chức năng chuyển đổi.
đo tốc độ

25
sử dụng PIL : cannot write mode LA as JPEG, tôi cần sử dụng chế độ L chứ không phải LA
jsky

6
Đây img = Image.open('image.png').convert('LA')cần phải đượcimg = Image.open('image.png').convert('L')
nviens

12
@BluePython: LAchế độ có độ sáng (độ sáng) và alpha. Nếu bạn sử dụng LAchế độ, thì đó greyscale.pngsẽ là hình ảnh RGBA với kênh alpha image.pngđược bảo tồn. Nếu bạn sử dụng Lchế độ, thì đó greyscale.pngsẽ là hình ảnh RGB (không có alpha).
unutbu

69

Bạn cũng có thể sử dụng scikit-image , cung cấp một số chức năng để chuyển đổi một hình ảnh ndarray, như thế nào rgb2gray.

from skimage import color
from skimage import io

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

Lưu ý : Các trọng lượng được sử dụng trong chuyển đổi này được hiệu chuẩn cho các phốt pho CRT hiện đại: Y = 0,2125 R + 0,7154 G + 0,0721 B

Ngoài ra, bạn có thể đọc hình ảnh trong thang độ xám bằng cách:

from skimage import io
img = io.imread('image.png', as_gray=True)

Có bình thường không khi tôi nhận được 0 <giá trị <1? Tôi có nên nhân chúng với 255 để có thang màu xám thật không?
Sam

biết rằng mục tiêu của tôi là sử dụng các tính năng GLCM (greycoprops)
Sam

Lưu ý cho io.imread: "as_grey" đã bị từ chối vì "as_gray". Cách sử dụng giống nhau, chỉ cần đánh vần là Americanized. :)
H halogen

1
Tôi tin rằng đây là câu trả lời hữu ích nhất cho câu hỏi, đầu ra của nó cũng tương thích với matplotlib và numpy.
Mert Beşiktepe

Tôi đang sử dụng đối tượng màu sắc nhưng hình ảnh của tôi bây giờ hơi đỏ và không phải màu xám (đen và trắng). Tôi cần sử dụng cmapnhư gray' then only the image is shown as gray in pyplot.imshow () `? Có suy nghĩ gì không? Tôi sai ở đâu
GadaaDhaariGeek

63

Ba trong số các phương pháp được đề xuất đã được kiểm tra tốc độ với 1000 hình ảnh PNG RGB (224 x 256 pixel) chạy với Python 3.5 trên Ubuntu 16.04 LTS (Xeon E5 2670 với SSD).

Thời gian chạy trung bình

pil : 1,037 giây

scipy: 1,040 giây

sk : 2.120 giây

PIL và SciPy đã đưa ra numpycác mảng giống hệt nhau (từ 0 đến 255). SkImage đưa ra các mảng từ 0 đến 1. Ngoài ra, màu sắc được chuyển đổi hơi khác nhau, xem ví dụ từ bộ dữ liệu CUB-200.

SkImage: SkImage

PIL : PIL

SciPy : Khoa học viễn tưởng

Original: Nguyên

Diff : nhập mô tả hình ảnh ở đây

  1. Hiệu suất

    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. Đầu ra
    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. So sánh
    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. Nhập khẩu
    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. Phiên bản
    skimage.version
    0.13.0
    scipy.version
    0.19.1
    np.version
    1.13.1

6
Hình ảnh I / O của SciPy đúng nghĩa là PIL / Gối. Do đó, việc kiểm tra SciPy đang kiểm tra lại PIL / Gối một cách hiệu quả với chi phí không đáng kể được giới thiệu bởi các chức năng bao bọc của SciPy. Nó đã có nhiều hơn hữu ích để thay thế OpenCV (mà không phải đòn bẩy PIL / Gối) cho scipy (mà không). Tuy nhiên, cảm ơn cho điểm chuẩn dành riêng! Sự chậm chạp rõ rệt do SciKit áp đặt là hấp dẫn ... và kinh hoàng.
Cecil Curry

@CecilCurry Cảm ơn ý tưởng của bạn với OpenCV! Tôi sẽ thêm nó khi tôi tìm thấy thời gian rảnh.
Maximilian Peters

Nâng cao! Không phải là một câu trả lời tôi đang tìm kiếm, nhưng dù sao cũng rất rất thú vị :)
Cyril N.

29

Bạn luôn có thể đọc tệp hình ảnh dưới dạng thang độ xám ngay từ đầu bằng cách sử dụng imreadtừ OpenCV:

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

Hơn nữa, trong trường hợp bạn muốn đọc hình ảnh dưới dạng RGB, hãy thực hiện một số xử lý và sau đó chuyển đổi sang Thang màu xám mà bạn có thể sử dụng cvtcolortừ OpenCV:

gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

6
Ftr: 0Cờ là cv2.CV_LOAD_IMAGE_GRAYSCALE.
dtk

24

Cách nhanh nhất và hiện tại là sử dụng Gối , cài đặt quapip install Pillow .

Mã này là:

from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')

3
lưu ý rằng, nếu bạn không xâu chuỗi các phương thức của mình như trong ví dụ trên, convertsẽ trả về một bản sao đã chuyển đổi của hình ảnh
Matt

không hoạt động cho PNG 32 bit, các giá trị sẽ được kẹp thành 255
Andrew Matuk

11

Hướng dẫn là gian lận vì nó bắt đầu bằng một hình ảnh thang độ xám được mã hóa bằng RGB, vì vậy họ chỉ cắt một kênh màu duy nhất và coi nó là thang độ xám. Các bước cơ bản bạn cần làm là chuyển đổi từ không gian màu RGB sang không gian màu mã hóa với thứ gì đó gần đúng với mô hình luma / chroma, chẳng hạn như YUV / YIQ hoặc HSL / HSV, sau đó cắt kênh giống như luma và sử dụng như hình ảnh thang độ xám của bạn. matplotlibdường như không cung cấp một cơ chế để chuyển đổi sang YUV / YIQ, nhưng nó cho phép bạn chuyển đổi sang HSV.

Hãy thử sử dụng matplotlib.colors.rgb_to_hsv(img)sau đó cắt giá trị cuối cùng (V) từ mảng cho thang độ xám của bạn. Nó không hoàn toàn giống như một giá trị luma, nhưng điều đó có nghĩa là bạn có thể làm tất cả trong đó matplotlib.

Lý lịch:

Ngoài ra, bạn có thể sử dụng PIL hoặc nội dung colorsys.rgb_to_yiq()để chuyển đổi sang không gian màu với giá trị độ sáng thực. Bạn cũng có thể đi vào và cuộn bộ chuyển đổi chỉ dành cho luma của riêng mình, mặc dù điều đó có thể là quá mức cần thiết.


9

Sử dụng công thức này

Y' = 0.299 R + 0.587 G + 0.114 B 

Chúng tôi có thể làm

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'))

Tuy nhiên, phần mềm chuyển đổi màu GIMP sang màu xám có ba thuật toán để thực hiện nhiệm vụ.


8

Nếu bạn đang sử dụng NumPy / SciPy, bạn cũng có thể sử dụng :

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


5
Cả hai scipy.ndimage.imread()scipy.misc.imread()được chính thức phản đối trong scipy 1.0.0 và sẽ được loại bỏ vĩnh viễn trong scipy 1.2.0. Mặc dù tài liệu của SciPy đề xuất imageio.imread()như một sự thay thế phù hợp, API của chức năng này là xương sống đến mức vô lý. Nó không cung cấp hỗ trợ cho chuyển đổi thang độ xám và do đó vẫn không phù hợp với nhiều ứng dụng - bao gồm cả ứng dụng của chúng tôi. </sigh>
Cecil Curry

5
@CecilCurry, làm thế nào để bạn chuyển đổi một hình ảnh màu theo tỷ lệ xám bằng imageio?
0x90

5

bạn có thể làm:

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()

5

Sử dụng img.Convert (), hỗ trợ cho LÊ LÊ, MẠNH RGB, và CMYK. chế độ

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)

Đầu ra:

[[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]]

1
dòng thứ 5 nên là img = img.convert('L')gì?
Allan Ruin

3

Tôi đã đến câu hỏi này thông qua Google, tìm kiếm một cách để chuyển đổi một hình ảnh đã được tải sang thang độ xám.

Đây là một cách để làm điều đó với 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
Đẹp. Tôi chỉ muốn lưu ý một giải pháp ngắn hơn sẽ làimg_gray = numpy.average(img, weights=[0.299, 0.587, 0.114], axis=2)
Akavall

@Akavall Rất vui được biết, cảm ơn bạn! Bạn có biết nếu phím tắt của bạn nhanh hơn? Nếu không, tôi sẽ giữ của tôi vì nó dễ hiểu hơn.
Martin Thoma

Tôi không có thời gian, cảm giác ruột của tôi numpy.averagenhanh hơn một chút nhưng thực tế không khác biệt. Giải pháp của bạn rất rõ ràng và có thông tin liên quan về R, G, B, vì vậy tôi sẽ giữ nó. Nhận xét của tôi là nhiều hơn một lựa chọn bổ sung, không phải là một thay thế.
Akavall

Cả hai scipy.ndimage.imread()scipy.misc.imread()được chính thức phản đối trong scipy 1.0.0 và sẽ được loại bỏ vĩnh viễn trong scipy 1.2.0. Thay vào đó, có lẽ bạn chỉ muốn sử dụng hỗ trợ chuyển đổi thang độ xám có sẵn của gối ( câu trả lời của ala unutbu ).
Cecil Curry

-3
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()

Bạn có thể sử dụng greyscale()trực tiếp cho việc chuyển đổi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.