# 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('\n' % height) 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()