Сохранение массива Numpy в виде изображения
У меня есть матрица в виде массива Numpy. Как бы я записал его на диск как образ? Работает любой формат (png, jpeg, bmp...). Одним из важных ограничений является то, что PIL отсутствует.
24 ответа
Вы можете использовать PyPNG. Это чистый Python (без зависимостей) открытый кодер / декодер PNG с открытым исходным кодом, и он поддерживает запись массивов NumPy в виде изображений.
Это использует 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)
- Первоначальный источник
- Смотрите также: Rust Port из этого вопроса.
- Пример использования благодаря Евгению Сергееву: /questions/11712639/sohranenie-massiva-numpy-v-vide-izobrazheniya/11712663#11712663
Если у вас есть 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, который он создает:
Предполагая, что вы хотите изображение в градациях серого:
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")