From 88833e8b03c4806a8b4bdff1684c8cddde851330 Mon Sep 17 00:00:00 2001 From: "Dmitry A. Grechka" Date: Fri, 19 Aug 2022 13:13:04 +0300 Subject: [PATCH] Migrating to imageio (#11) * fixing handling of monochrome images. Transitioning to imageio. Avoiding disk IO * adding missing data files --- pyproject.toml | 2 +- src/kafkajobs/serialization/__init__.py | 2 +- src/kafkajobs/serialization/image.py | 79 ++++++++++-------------- tests/data/343911.jpg | Bin 0 -> 36166 bytes tests/data/monochrome.png | Bin 0 -> 13546 bytes tests/test_serialization.py | 26 +++++++- 6 files changed, 60 insertions(+), 49 deletions(-) create mode 100755 tests/data/343911.jpg create mode 100755 tests/data/monochrome.png diff --git a/pyproject.toml b/pyproject.toml index bb66e2c..0be8708 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ ] dependencies = [ 'kafka-python == 2.0.2', - 'scikit-image == 0.19.3' + 'imageio == 2.21.1' ] [tool.hatch.build.targets.wheel] diff --git a/src/kafkajobs/serialization/__init__.py b/src/kafkajobs/serialization/__init__.py index 940bfe8..e44e659 100644 --- a/src/kafkajobs/serialization/__init__.py +++ b/src/kafkajobs/serialization/__init__.py @@ -1,2 +1,2 @@ -from .image import imagesFieldToNp, imagesNpToStrList +from .image import imagesFieldToNp, imagesNpToStrList, imageNpToB64SerializedStruct, imageB64SerializedStructToNp from .np import base64strToNpArray, npArrayToBase64str \ No newline at end of file diff --git a/src/kafkajobs/serialization/image.py b/src/kafkajobs/serialization/image.py index 4d1b459..dc7c18e 100644 --- a/src/kafkajobs/serialization/image.py +++ b/src/kafkajobs/serialization/image.py @@ -1,53 +1,40 @@ -from skimage import io -import tempfile -import os +import imageio.v3 as iio +import numpy as np import base64 -import shutil +def imageToRgbImage(image): + rank = len(image.shape) + if rank == 2: + # monochrome + # adding color channels + return np.stack((image, image, image), axis=2) + else: + # coloured + return image[:,:,:3] # discarding alpha channel if any + +def imageNpToB64SerializedStruct(npImage): + rgbNumpyImage = imageToRgbImage(npImage) + jpg_encoded_bytes = iio.imwrite("", rgbNumpyImage, extension=".jpeg") + + image = { + 'type': "jpg", + 'data': base64.encodebytes(jpg_encoded_bytes).decode("utf-8").replace("\n","") + } + return image def imagesNpToStrList(npImages): - tempDir = tempfile.mkdtemp() - try: - idx1 = 0 - images = [] - # encoding images - for npImage in npImages: - photoPath = os.path.join(tempDir,"{0}.jpeg".format(idx1)) - io.imsave(photoPath, npImage[:,:,:3]) # discarding alpha channel if any - #print("image {0} saved".format(photoPath)) - with open(photoPath, 'rb') as photoFile: - photo = photoFile.read() - #print("image {0} read".format(photoPath)) - image = { - 'type': "jpg", - 'data': base64.encodebytes(photo).decode("utf-8").replace("\n","") - } - images.append(image) - idx1 += 1 - return images - finally: - shutil.rmtree(tempDir) + """Obsolete: left for backward compatibility""" + return [imageNpToB64SerializedStruct(x) for x in npImages] + +def imageB64SerializedStructToNp(b64SerializedStruct): + imgType = b64SerializedStruct['type'] + image_b64 : str = b64SerializedStruct['data'] + imageData = base64.decodebytes(image_b64.encode("utf-8")) + imNumpy = iio.imread(imageData, extension=f".{imgType}") + # guard againes old version of encoders + return imageToRgbImage(imNumpy) def imagesFieldToNp(images): - tempDir = tempfile.mkdtemp() - try: - imgIdx = 0 - imagesNp = [] - # decoding images - for image in images: - imgType = image['type'] - image_b64 : str = image['data'] - imageData = base64.decodebytes(image_b64.encode("utf-8")) - imageFilePath = os.path.join(tempDir,"{0}.{1}".format(imgIdx,imgType)) - with open(imageFilePath, "wb") as file1: - file1.write(imageData) - try: - imNumpy = io.imread(imageFilePath) - imagesNp.append(imNumpy) - except Exception as exc1: - print("Error calulating hash for one of the images ({0})".format(exc1)) - imgIdx += 1 - return imagesNp - finally: - shutil.rmtree(tempDir) + """Obsolete: left for backward compatibility""" + return [imageB64SerializedStructToNp(x) for x in images] diff --git a/tests/data/343911.jpg b/tests/data/343911.jpg new file mode 100755 index 0000000000000000000000000000000000000000..abf6ed8f27d8361bf1b8796c1e047c2a5fe7bf47 GIT binary patch literal 36166 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 PKQXB&XuhqIGyC{|X)G)V literal 0 HcmV?d00001 diff --git a/tests/test_serialization.py b/tests/test_serialization.py index 84054c9..40277b3 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -1,14 +1,38 @@ import unittest +import base64 + import kafkajobs.serialization as serialization + import imageio.v2 as imageio +import numpy as np class TestSerialization(unittest.TestCase): + def test_image_round_trip(self): + nparr = imageio.imread('tests/data/343911.jpg') + shape = nparr.shape + + roundTripped = serialization.imageB64SerializedStructToNp(serialization.imageNpToB64SerializedStruct(nparr)) + + assert roundTripped.shape == shape, f'shape {roundTripped.shape} is not {shape}' + def test_issue8(self): nparr = imageio.imread('tests/data/issue_8.png') #print(f'shape {nparr.shape}') - serialized = serialization.imagesNpToStrList([nparr]) + serialized = serialization.imagesNpToStrList([nparr]) + + def test_deserializing_monochrome(self): + # manual encoding + with open('tests/data/monochrome.png', 'rb') as photoFile: + photo = photoFile.read() + image = { + 'type': "jpg", + 'data': base64.encodebytes(photo).decode("utf-8").replace("\n","") + } + + deserialized = serialization.imageB64SerializedStructToNp(image) # this one creates 3 colour channels + assert deserialized.shape == (400, 400, 3), f'shape {deserialized.shape}' if __name__ == '__main__': unittest.main()