2021-05-05 03:30:15 +02:00
|
|
|
# Copyright 2021 The MediaPipe Authors.
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
"""Tests for mediapipe.python._framework_bindings.image."""
|
|
|
|
|
|
|
|
import gc
|
2022-09-29 12:42:07 +02:00
|
|
|
import os
|
2021-05-05 03:30:15 +02:00
|
|
|
import random
|
|
|
|
import sys
|
2022-09-06 23:29:51 +02:00
|
|
|
|
2021-05-05 03:30:15 +02:00
|
|
|
from absl.testing import absltest
|
|
|
|
import cv2
|
|
|
|
import numpy as np
|
|
|
|
import PIL.Image
|
|
|
|
|
2022-09-29 12:42:07 +02:00
|
|
|
# resources dependency
|
2022-09-06 23:29:51 +02:00
|
|
|
from mediapipe.python._framework_bindings import image
|
|
|
|
from mediapipe.python._framework_bindings import image_frame
|
|
|
|
|
|
|
|
Image = image.Image
|
|
|
|
ImageFormat = image_frame.ImageFormat
|
|
|
|
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
# TODO: Add unit tests specifically for memory management.
|
|
|
|
class ImageTest(absltest.TestCase):
|
|
|
|
|
|
|
|
def test_create_image_from_gray_cv_mat(self):
|
|
|
|
w, h = random.randrange(3, 100), random.randrange(3, 100)
|
|
|
|
mat = cv2.cvtColor(
|
|
|
|
np.random.randint(2**8 - 1, size=(h, w, 3), dtype=np.uint8),
|
|
|
|
cv2.COLOR_RGB2GRAY)
|
|
|
|
mat[2, 2] = 42
|
2022-09-06 23:29:51 +02:00
|
|
|
gray8_image = Image(image_format=ImageFormat.GRAY8, data=mat)
|
|
|
|
self.assertTrue(np.array_equal(mat, gray8_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'index dimension mismatch'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(gray8_image[w, h, 1])
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'out of bounds'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(gray8_image[w, h])
|
|
|
|
self.assertEqual(42, gray8_image[2, 2])
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
def test_create_image_from_rgb_cv_mat(self):
|
|
|
|
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
|
|
|
|
mat = cv2.cvtColor(
|
|
|
|
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
|
|
|
|
cv2.COLOR_RGB2BGR)
|
|
|
|
mat[2, 2, 1] = 42
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb_image = Image(image_format=ImageFormat.SRGB, data=mat)
|
|
|
|
self.assertTrue(np.array_equal(mat, rgb_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'out of bounds'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(rgb_image[w, h, channels])
|
|
|
|
self.assertEqual(42, rgb_image[2, 2, 1])
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
def test_create_image_from_rgb48_cv_mat(self):
|
|
|
|
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
|
|
|
|
mat = cv2.cvtColor(
|
|
|
|
np.random.randint(2**16 - 1, size=(h, w, channels), dtype=np.uint16),
|
|
|
|
cv2.COLOR_RGB2BGR)
|
|
|
|
mat[2, 2, 1] = 42
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb48_image = Image(image_format=ImageFormat.SRGB48, data=mat)
|
|
|
|
self.assertTrue(np.array_equal(mat, rgb48_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'out of bounds'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(rgb48_image[w, h, channels])
|
|
|
|
self.assertEqual(42, rgb48_image[2, 2, 1])
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
def test_create_image_from_gray_pil_image(self):
|
|
|
|
w, h = random.randrange(3, 100), random.randrange(3, 100)
|
|
|
|
img = PIL.Image.fromarray(
|
|
|
|
np.random.randint(2**8 - 1, size=(h, w), dtype=np.uint8), 'L')
|
2022-09-06 23:29:51 +02:00
|
|
|
gray8_image = Image(image_format=ImageFormat.GRAY8, data=np.asarray(img))
|
|
|
|
self.assertTrue(np.array_equal(np.asarray(img), gray8_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'index dimension mismatch'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(gray8_image[w, h, 1])
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'out of bounds'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(gray8_image[w, h])
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
def test_create_image_from_rgb_pil_image(self):
|
|
|
|
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
|
|
|
|
img = PIL.Image.fromarray(
|
|
|
|
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
|
|
|
|
'RGB')
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb_image = Image(image_format=ImageFormat.SRGB, data=np.asarray(img))
|
|
|
|
self.assertTrue(np.array_equal(np.asarray(img), rgb_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'out of bounds'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(rgb_image[w, h, channels])
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
def test_create_image_from_rgba64_pil_image(self):
|
|
|
|
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 4
|
|
|
|
img = PIL.Image.fromarray(
|
|
|
|
np.random.randint(2**16 - 1, size=(h, w, channels), dtype=np.uint16),
|
|
|
|
'RGBA')
|
2022-09-06 23:29:51 +02:00
|
|
|
rgba_image = Image(
|
|
|
|
image_format=ImageFormat.SRGBA64,
|
2021-07-24 02:09:32 +02:00
|
|
|
data=np.asarray(img).astype(np.uint16))
|
2022-09-06 23:29:51 +02:00
|
|
|
self.assertTrue(np.array_equal(np.asarray(img), rgba_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
with self.assertRaisesRegex(IndexError, 'out of bounds'):
|
2022-09-06 23:29:51 +02:00
|
|
|
print(rgba_image[1000, 1000, 1000])
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
def test_image_numby_view(self):
|
|
|
|
w, h, channels = random.randrange(3, 100), random.randrange(3, 100), 3
|
|
|
|
mat = cv2.cvtColor(
|
|
|
|
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
|
|
|
|
cv2.COLOR_RGB2BGR)
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb_image = Image(image_format=ImageFormat.SRGB, data=mat)
|
|
|
|
output_ndarray = rgb_image.numpy_view()
|
|
|
|
self.assertTrue(np.array_equal(mat, rgb_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
# The output of numpy_view() is a reference to the internal data and it's
|
|
|
|
# unwritable after creation.
|
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
'assignment destination is read-only'):
|
|
|
|
output_ndarray[0, 0, 0] = 0
|
|
|
|
copied_ndarray = np.copy(output_ndarray)
|
|
|
|
copied_ndarray[0, 0, 0] = 0
|
|
|
|
|
|
|
|
def test_cropped_gray8_image(self):
|
|
|
|
w, h = random.randrange(20, 100), random.randrange(20, 100)
|
|
|
|
channels, offset = 3, 10
|
|
|
|
mat = cv2.cvtColor(
|
|
|
|
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
|
|
|
|
cv2.COLOR_RGB2GRAY)
|
2022-09-06 23:29:51 +02:00
|
|
|
gray8_image = Image(
|
|
|
|
image_format=ImageFormat.GRAY8,
|
2021-05-05 03:30:15 +02:00
|
|
|
data=np.ascontiguousarray(mat[offset:-offset, offset:-offset]))
|
|
|
|
self.assertTrue(
|
2022-09-06 23:29:51 +02:00
|
|
|
np.array_equal(mat[offset:-offset, offset:-offset],
|
|
|
|
gray8_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
def test_cropped_rgb_image(self):
|
|
|
|
w, h = random.randrange(20, 100), random.randrange(20, 100)
|
|
|
|
channels, offset = 3, 10
|
|
|
|
mat = cv2.cvtColor(
|
|
|
|
np.random.randint(2**8 - 1, size=(h, w, channels), dtype=np.uint8),
|
|
|
|
cv2.COLOR_RGB2BGR)
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb_image = Image(
|
|
|
|
image_format=ImageFormat.SRGB,
|
2021-05-05 03:30:15 +02:00
|
|
|
data=np.ascontiguousarray(mat[offset:-offset, offset:-offset, :]))
|
|
|
|
self.assertTrue(
|
|
|
|
np.array_equal(mat[offset:-offset, offset:-offset, :],
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
# For image frames that store contiguous data, the output of numpy_view()
|
|
|
|
# points to the pixel data of the original image frame object. The life cycle
|
|
|
|
# of the data array should tie to the image frame object.
|
|
|
|
def test_image_numpy_view_with_contiguous_data(self):
|
|
|
|
w, h = 640, 480
|
|
|
|
mat = np.random.randint(2**8 - 1, size=(h, w, 3), dtype=np.uint8)
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb_image = Image(image_format=ImageFormat.SRGB, data=mat)
|
|
|
|
self.assertTrue(rgb_image.is_contiguous())
|
|
|
|
initial_ref_count = sys.getrefcount(rgb_image)
|
|
|
|
self.assertTrue(np.array_equal(mat, rgb_image.numpy_view()))
|
2021-05-05 03:30:15 +02:00
|
|
|
# Get 2 data array objects and verify that the image frame's ref count is
|
|
|
|
# increased by 2.
|
2022-09-06 23:29:51 +02:00
|
|
|
np_view = rgb_image.numpy_view()
|
|
|
|
self.assertEqual(sys.getrefcount(rgb_image), initial_ref_count + 1)
|
|
|
|
np_view2 = rgb_image.numpy_view()
|
|
|
|
self.assertEqual(sys.getrefcount(rgb_image), initial_ref_count + 2)
|
2021-05-05 03:30:15 +02:00
|
|
|
del np_view
|
|
|
|
del np_view2
|
|
|
|
gc.collect()
|
|
|
|
# After the two data array objects getting destroyed, the current ref count
|
|
|
|
# should euqal to the initial ref count.
|
2022-09-06 23:29:51 +02:00
|
|
|
self.assertEqual(sys.getrefcount(rgb_image), initial_ref_count)
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
# For image frames that store non contiguous data, the output of numpy_view()
|
|
|
|
# stores a copy of the pixel data of the image frame object. The life cycle of
|
|
|
|
# the data array doesn't tie to the image frame object.
|
|
|
|
def test_image_numpy_view_with_non_contiguous_data(self):
|
|
|
|
w, h = 641, 481
|
|
|
|
mat = np.random.randint(2**8 - 1, size=(h, w, 3), dtype=np.uint8)
|
2022-09-06 23:29:51 +02:00
|
|
|
rgb_image = Image(image_format=ImageFormat.SRGB, data=mat)
|
|
|
|
self.assertFalse(rgb_image.is_contiguous())
|
|
|
|
initial_ref_count = sys.getrefcount(rgb_image)
|
|
|
|
self.assertTrue(np.array_equal(mat, rgb_image.numpy_view()))
|
|
|
|
np_view = rgb_image.numpy_view()
|
|
|
|
self.assertEqual(sys.getrefcount(rgb_image), initial_ref_count)
|
2021-05-05 03:30:15 +02:00
|
|
|
del np_view
|
|
|
|
gc.collect()
|
2022-09-06 23:29:51 +02:00
|
|
|
self.assertEqual(sys.getrefcount(rgb_image), initial_ref_count)
|
2021-05-05 03:30:15 +02:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
absltest.main()
|