Сохранение массива Numpy в виде изображения

У меня есть матрица в виде массива Numpy. Как бы я записал его на диск как образ? Работает любой формат (png, jpeg, bmp...). Одним из важных ограничений является то, что PIL отсутствует.

24 ответа

Решение

Вы можете использовать PyPNG. Это чистый Python (без зависимостей) открытый кодер / декодер PNG с открытым исходным кодом, и он поддерживает запись массивов NumPy в виде изображений.

Ответ с использованием PIL (на всякий случай, если он полезен).

учитывая массив N "n"

from PIL import Image
im = Image.fromarray(A)
im.save("your_file.jpeg")

Вы можете заменить "JPEG" практически любой формат, который вы хотите. Подробнее о форматах здесь

Это использует PIL, но, возможно, некоторые могут найти это полезным:

import scipy.misc
scipy.misc.imsave('outfile.jpg', image_array)

РЕДАКТИРОВАТЬ: текущий scipy Версия начала нормализовать все изображения так, чтобы min(данные) становились черными, а max(данные) - белыми. Это нежелательно, если данные должны иметь точные уровни серого или точные каналы RGB. Решение:

import scipy.misc
scipy.misc.toimage(image_array, cmin=0.0, cmax=...).save('outfile.jpg')

С matplotlib:

import matplotlib

matplotlib.image.imsave('name.png', array)

Работает с Matplotlib 1.3.1, я не знаю о более низкой версии. Из строки документации:

Arguments:
  *fname*:
    A string containing a path to a filename, or a Python file-like object.
    If *format* is *None* and *fname* is a string, the output
    format is deduced from the extension of the filename.
  *arr*:
    An MxN (luminance), MxNx3 (RGB) or MxNx4 (RGBA) array.

Там в opencv для python ( http://docs.opencv.org/trunk/doc/py_tutorials/py_tutorials.html).

import cv2
import numpy as np

cv2.imwrite("filename.png", np.zeros((10,10)))

полезно, если вам нужно больше обрабатывать, чем сохранять.

Pure Python (2 и 3), фрагмент без сторонних зависимостей.

Эта функция записывает сжатый, истинный цвет (4 байта на пиксель) RGBA PNG-х.

def write_png(buf, width, height):
    """ buf: must be bytes or a bytearray in Python3.x,
        a regular string in Python2.x.
    """
    import zlib, struct

    # reverse the vertical line order and add null bytes at the start
    width_byte_4 = width * 4
    raw_data = b''.join(
        b'\x00' + buf[span:span + width_byte_4]
        for span in range((height - 1) * width_byte_4, -1, - width_byte_4)
    )

    def png_pack(png_tag, data):
        chunk_head = png_tag + data
        return (struct.pack("!I", len(data)) +
                chunk_head +
                struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head)))

    return b''.join([
        b'\x89PNG\r\n\x1a\n',
        png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, 6, 0, 0, 0)),
        png_pack(b'IDAT', zlib.compress(raw_data, 9)),
        png_pack(b'IEND', b'')])

... Данные должны быть записаны непосредственно в файл, открытый в двоичном виде, как в:

data = write_png(buf, 64, 64)
with open("my_image.png", 'wb') as fd:
    fd.write(data)

Если у вас есть matplotlib, вы можете сделать:

import matplotlib.pyplot as plt
plt.imshow(matrix) #Needs to be in row,col order
plt.savefig(filename)

Это сохранит сюжет (не сами изображения).

Для сохранения массива numpy как изображения у U есть несколько вариантов:

1) лучшее из других: OpenCV

 import cv2   
 cv2.imwrite('file name with extension(like .jpg)', numpy_array)

2) Матплотлиб

  from matplotlib import pyplot as plt
  plt.imsave('file name with extension(like .jpg)', numpy_array)

3) PIL

  from PIL import Image
  image = Image.fromarray(numpy_array)
  image.save('file name with extension(like .jpg)')

4)...

scipy.misc дает предупреждение об устаревании imsave функция и предлагает использование imageio вместо.

import imageio
imageio.imwrite('image_name.png', img)

Вы можете использовать библиотеку 'Skimage' в Python

Пример:

from skimage.io import imsave
imsave('Path_to_your_folder/File_name.jpg',your_array)

Приложение к ответу @ideasman42:

def saveAsPNG(array, filename):
    import struct
    if any([len(row) != len(array[0]) for row in array]):
        raise ValueError, "Array should have elements of equal size"

                                #First row becomes top row of image.
    flat = []; map(flat.extend, reversed(array))
                                 #Big-endian, unsigned 32-byte integer.
    buf = b''.join([struct.pack('>I', ((0xffFFff & i32)<<8)|(i32>>24) )
                    for i32 in flat])   #Rotate from ARGB to RGBA.

    data = write_png(buf, len(array[0]), len(array))
    f = open(filename, 'wb')
    f.write(data)
    f.close()

Так что вы можете сделать:

saveAsPNG([[0xffFF0000, 0xffFFFF00],
           [0xff00aa77, 0xff333333]], 'test_grid.png')

производства test_grid.png:

Сетка красная, желтая, темно-аква, серая

(Прозрачность также работает, уменьшая старший байт из 0xff.)

Для тех, кто ищет прямой полностью рабочий пример:

from PIL import Image
import numpy

w,h = 200,100
img = numpy.zeros((h,w,3),dtype=numpy.uint8) # has to be unsigned bytes

img[:] = (0,0,255) # fill blue

x,y = 40,20
img[y:y+30, x:x+50] = (255,0,0) # 50x30 red box

Image.fromarray(img).convert("RGB").save("art.png") # don't need to convert

Кроме того, если вы хотите высокое качество JPEG
.save(file, subsampling=0, quality=100)

matplotlib svn имеет новую функцию для сохранения изображений как просто изображения - без осей и т. д. Это очень простая функция для бэкпорта, если вы не хотите устанавливать svn (скопировано прямо из image.py в matplotlib svn, удалили документация для краткости):

def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None, origin=None):
    from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
    from matplotlib.figure import Figure

    fig = Figure(figsize=arr.shape[::-1], dpi=1, frameon=False)
    canvas = FigureCanvas(fig)
    fig.figimage(arr, cmap=cmap, vmin=vmin, vmax=vmax, origin=origin)
    fig.savefig(fname, dpi=1, format=format)

Imageio - это библиотека Python, предоставляющая простой интерфейс для чтения и записи широкого спектра данных изображений, включая анимированные изображения, видео, объемные данные и научные форматы. Он кроссплатформенный, работает на Python 2.7 и 3.4+ и прост в установке.

Это пример для изображения в градациях серого:

import numpy as np
import imageio

# data is numpy array with grayscale value for each pixel.
data = np.array([70,80,82,72,58,58,60,63,54,58,60,48,89,115,121,119])

# 16 pixels can be converted into square of 4x4 or 2x8 or 8x2
data = data.reshape((4, 4)).astype('uint8')

# save image
imageio.imwrite('pic.jpg', data)

Мир, вероятно, не нуждается в еще одном пакете для записи массива в PNG-файл, но для тех, кто не может получить достаточно, я недавно выложил numpngw на github:

https://github.com/WarrenWeckesser/numpngw

и на pypi: https://pypi.python.org/pypi/numpngw/

Единственная внешняя зависимость - тупая.

Вот первый пример из examples каталог хранилища. Основная линия просто

write_png('example1.png', img)

где img это массив NumPy. Весь код перед этой строкой - операторы импорта и код для создания img,

import numpy as np
from numpngw import write_png


# Example 1
#
# Create an 8-bit RGB image.

img = np.zeros((80, 128, 3), dtype=np.uint8)

grad = np.linspace(0, 255, img.shape[1])

img[:16, :, :] = 127
img[16:32, :, 0] = grad
img[32:48, :, 1] = grad[::-1]
img[48:64, :, 2] = grad
img[64:, :, :] = 127

write_png('example1.png', img)

Вот файл PNG, который он создает:

example1.png

Предполагая, что вы хотите изображение в градациях серого:

im = Image.new('L', (width, height))
im.putdata(an_array.flatten().tolist())
im.save("image.tiff")

В следующем ответе есть методы, предложенные @Nima Farhadi для измерения времени.

Самый быстрый — CV2 , но важно изменить порядок цветов с RGB на BGR. Простым является matplotlib.

Важно убедиться, что массив имеет целочисленный формат без знака uint8/16/32.

Код:

      #Matplotlib
from matplotlib import pyplot as plt
plt.imsave('c_plt.png', c.astype(np.uint8))

#PIL
from PIL import Image
image = Image.fromarray(c.astype(np.uint8))
image.save('c_pil.png')


#CV2, OpenCV
import cv2
cv2.imwrite('c_cv2.png', cv2.cvtColor(c, cv2.COLOR_RGB2BGR))

Использование cv2.imwrite,

import cv2
assert mat.shape[2] == 1 or mat.shape[2] == 3, 'the third dim should be channel'
cv2.imwrite(path, mat) # note the form of data should be height - width - channel  

Если вы работаете в среде Python Spyder, то это не может быть проще, чем просто щелкнуть правой кнопкой мыши массив в проводнике переменных, а затем выбрать параметр Показать изображение.

Это попросит вас сохранить изображение в dsik, в основном в формате PNG.

Библиотека PIL в этом случае не понадобится.

Если вы уже используете [Py]Qt, вас может заинтересовать qimage2ndarray. Начиная с версии 1.4 (только что выпущенной), PySide также поддерживается, и будет крошечный imsave(filename, array) функция похожа на scipy's, но использует Qt вместо PIL. С 1.3 просто используйте что-то вроде следующего:

qImage = array2qimage(image, normalize = False) # create QImage from ndarray
success = qImage.save(filename) # use Qt's image IO functions for saving PNG/JPG/..

(Другое преимущество 1.4 заключается в том, что это чисто Python-решение, которое делает его еще более легким.)

Я прилагаю простую процедуру для преобразования npy в изображение. Работает на 100%, и это кусок пирога!

из PIL импорта изображений импорта matplotlib

img = np.load('flair1_slice75.npy')

matplotlib.image.imsave("G1_flair_75.jpeg", изображение)

Основываясь на ответе @ideasman42 и дополнении Евгения Сергеева, вот реализация преобразования массивов numpy в изображения.

Примечание: эта реализация ставитa[1,1,:]в левом верхнем углу изображения

Чтобы демистифицировать формат png, я вставил некоторую информацию из http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html .

эта реализация отображает:

  • массивы nxm с использованием палитры и масштабированием значений
  • RGB-массивы nxmx3 с одним цветом прозрачности; игнорирует палитру
  • массивы nxmx4 RGBA; игнорирует палитру и прозрачный цвет

он возвращает строку байтов, которую можно передать в PhotoImage. Это не так строго, как написано ниже Уорреном Векессером, но данные в tkinter передаются прозрачным образом.

      # https://stackoverflow.com/questions/902761/saving-a-numpy-array-as-an-image
# http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
# we insist on
#  o values between 0-1
#  o g, ga, rgb, rgba
#  o nxmx 1/2/3/3
import numpy as np
import zlib, struct

def topngbytes(a:np.ndarray,transparentcolor=[1,2,3],hicolor=[255,95,0]):
    sz = a.shape
    lensz=len(sz)
    height,width=sz[0:2]
    colortype=0
    palette=None

    # Color    Allowed    Interpretation
    #    Type    Bit Depths
    #
    #    0       1,2,4,8,16  Each pixel is a grayscale sample.
    #    2       8,16        Each pixel is an R,G,B triple.
    #    3       1,2,4,8     Each pixel is a palette index;
    #                        a PLTE chunk must appear.
    #    4       8,16        Each pixel is a grayscale sample,
    #                        followed by an alpha sample.
    #    6       8,16        Each pixel is an R,G,B triple,
    #                        followed by an alpha sample.
    if lensz==2:
        colortype=3        # 8bit palette
        amin=np.min(a)
        da=np.max(a)-amin
        if da==0: a=a*0+127
        elif da<72 or da>255: a=255/da*(a-amin)

        cmin = np.array(transparentcolor,dtype=float)  # generate a two tone palette
        dc = hicolor - cmin
        palette = np.zeros(shape=(256, 3), dtype=np.uint8)
        for i, r in enumerate(palette): r[:] = cmin + dc/255. * i
            
    elif lensz==3:
        n=sz[-1]                    #color info always the last dimension
        if n==2: colortype=4        # grey+alpha
        elif n==3: colortype=2      # rgb
        elif n==4: colortype=6      # rgba
        else: raise(ValueError(f"mImg: color dimension must be nxmx 1,2,3 or 4, not {sz}"))

    buf = b''.join( b'\x00' + row.tobytes() for row in a.astype(dtype=np.uint8))


    def png_pack(png_tag, data):
        chunk_head = png_tag + data
        return  struct.pack("!I", len(data)) + \
                chunk_head + \
                struct.pack("!I", 0xFFFFFFFF & zlib.crc32(chunk_head))

    # first chunc is    IHDR
    # Width:              4 bytes
    # Height:             4 bytes
    # Bit depth:          1 byte  --> we use 8  = 0-255
    # Color type:         1 byte
    # Compression method: 1 byte
    # Filter method:      1 byte
    # Interlace method:   1 byte

    # The PLTE chunk contains from 1 to 256 palette entries, each a three-byte series of the form:
    #
    # Red:   1 byte (0 = black, 255 = red)
    # Green: 1 byte (0 = black, 255 = green)
    # Blue:  1 byte (0 = black, 255 = blue)
    # The number of entries is determined from the chunk length. A chunk length not divisible by 3 is an error.
    #
    # This chunk must appear for color type 3, and can appear for color types 2 and 6;
    # it must not appear for color types 0 and 4. If this chunk does appear, it must precede the first IDAT chunk.
    # There must not be more than one PLTE chunk.
    #
    # For color type 3 (indexed color), the PLTE chunk is required.
    # The first entry in PLTE is referenced by pixel value 0

    IHDR=png_pack(b'IHDR', struct.pack("!2I5B", width, height, 8, colortype, 0, 0, 0))
    PLTE = b'' if (colortype in [0,4]) or palette is None else png_pack(b'PLTE',palette.tobytes())
    t=transparentcolor
    tRNS = png_pack(b'tRNS', struct.pack("!6B", t[0], 0, t[1], 0, t[2], 0))
    IDAT = png_pack(b'IDAT', zlib.compress(buf, 9))
    IEND = png_pack(b'IEND', b'')

    return b''.join([b'\x89PNG\r\n\x1a\n',IHDR,PLTE,tRNS,IDAT,IEND])

С Pygame

так что это должно работать, как я тестировал (у вас должен быть установлен pygame, если у вас нет pygame, установите его с помощью pip -> pip install pygame (это иногда не работает, поэтому в этом случае вам придется загрузить колесо или что-то, но что вы можете посмотреть в гугле)):

import pygame


pygame.init()
win = pygame.display.set_mode((128, 128))
pygame.surfarray.blit_array(win, yourarray)
pygame.display.update()
pygame.image.save(win, 'yourfilename.png')

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

вот пример, запустите этот код:

import pygame
from numpy import zeros


pygame.init()
win = pygame.display.set_mode((128, 128))
striped = zeros((128, 128, 3))
striped[:] = (255, 0, 0)
striped[:, ::3] = (0, 255, 255)
pygame.surfarray.blit_array(win, striped)
pygame.display.update()
pygame.image.save(win, 'yourfilename.png')

Вы можете использовать этот код для преобразования ваших данных Npy в изображение:

      from PIL import Image
import numpy as np
data = np.load('/kaggle/input/objects-dataset/nmbu.npy')
im = Image.fromarray(data, 'RGB')
im.save("your_file.jpeg")
Другие вопросы по тегам