# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2014-2017 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ############################################################################*/
"""Function to save an image to a file."""
__authors__ = ["T. Vincent"]
__license__ = "MIT"
__date__ = "03/04/2017"
import base64
import struct
import sys
import zlib
# Image writer ################################################################
def convertRGBDataToPNG(data):
"""Convert a RGB bitmap to PNG.
It only supports RGB bitmap with one byte per channel stored as a 3D array.
See `Definitive Guide `_ and
`Specification `_ for details.
:param data: A 3D array (h, w, rgb) storing an RGB image
:type data: numpy.ndarray of unsigned bytes
:returns: The PNG encoded data
:rtype: bytes
"""
height, width = data.shape[0], data.shape[1]
depth = 8 # 8 bit per channel
colorType = 2 # 'truecolor' = RGB
interlace = 0 # No
IHDRdata = struct.pack(">ccccIIBBBBB", b'I', b'H', b'D', b'R',
width, height, depth, colorType,
0, 0, interlace)
# Add filter 'None' before each scanline
preparedData = b'\x00' + b'\x00'.join(line.tostring() for line in data)
compressedData = zlib.compress(preparedData, 8)
IDATdata = struct.pack("cccc", b'I', b'D', b'A', b'T')
IDATdata += compressedData
return b''.join([
b'\x89PNG\r\n\x1a\n', # PNG signature
# IHDR chunk: Image Header
struct.pack(">I", 13), # length
IHDRdata,
struct.pack(">I", zlib.crc32(IHDRdata) & 0xffffffff), # CRC
# IDAT chunk: Payload
struct.pack(">I", len(compressedData)),
IDATdata,
struct.pack(">I", zlib.crc32(IDATdata) & 0xffffffff), # CRC
b'\x00\x00\x00\x00IEND\xaeB`\x82' # IEND chunk: footer
])
def saveImageToFile(data, fileNameOrObj, fileFormat):
"""Save a RGB image to a file.
:param data: A 3D array (h, w, 3) storing an RGB image.
:type data: numpy.ndarray with of unsigned bytes.
:param fileNameOrObj: Filename or object to use to write the image.
:type fileNameOrObj: A str or a 'file-like' object with a 'write' method.
:param str fileFormat: The type of the file in: 'png', 'ppm', 'svg', 'tiff'.
"""
assert len(data.shape) == 3
assert data.shape[2] == 3
assert fileFormat in ('png', 'ppm', 'svg', 'tiff')
if not hasattr(fileNameOrObj, 'write'):
if sys.version_info < (3, ):
fileObj = open(fileNameOrObj, "wb")
else:
if fileFormat in ('png', 'ppm', 'tiff'):
# Open in binary mode
fileObj = open(fileNameOrObj, 'wb')
else:
fileObj = open(fileNameOrObj, 'w', newline='')
else: # Use as a file-like object
fileObj = fileNameOrObj
if fileFormat == 'svg':
height, width = data.shape[:2]
base64Data = base64.b64encode(convertRGBDataToPNG(data))
fileObj.write(
'\n')
fileObj.write('\n')
fileObj.write('')
elif fileFormat == 'ppm':
height, width = data.shape[:2]
fileObj.write(b'P6\n')
fileObj.write(b'%d %d\n' % (width, height))
fileObj.write(b'255\n')
fileObj.write(data.tostring())
elif fileFormat == 'png':
fileObj.write(convertRGBDataToPNG(data))
elif fileFormat == 'tiff':
if fileObj == fileNameOrObj:
raise NotImplementedError(
'Save TIFF to a file-like object not implemented')
from silx.third_party.TiffIO import TiffIO
tif = TiffIO(fileNameOrObj, mode='wb+')
tif.writeImage(data, info={'Title': 'OpenGL Plot Snapshot'})
if fileObj != fileNameOrObj:
fileObj.close()