pytorch
428 строк · 16.9 Кб
1
2
3
4
5
6import unittest7try:8import cv29import lmdb10except ImportError:11pass # Handled below12
13from PIL import Image14import numpy as np15import shutil16import io17import sys18import tempfile19
20# TODO: This test does not test scaling because
21# the algorithms used by OpenCV in the C and Python
22# version seem to differ slightly. It does test
23# most other features
24
25from hypothesis import given, settings, Verbosity26import hypothesis.strategies as st27
28from caffe2.proto import caffe2_pb229import caffe2.python.hypothesis_test_util as hu30
31from caffe2.python import workspace, core32
33
34# Verification routines (applies transformations to image to
35# verify if the operator produces same result)
36def verify_apply_bounding_box(img, box):37import skimage.util38if any(type(box[f]) is not int or np.isnan(box[f] or box[f] < 0)39for f in range(0, 4)):40return img41# Box is ymin, xmin, bound_height, bound_width42y_bounds = (box[0], img.shape[0] - box[0] - box[2])43x_bounds = (box[1], img.shape[1] - box[1] - box[3])44c_bounds = (0, 0)45
46if any(el < 0 for el in list(y_bounds) + list(x_bounds) + list(c_bounds)):47return img48
49bboxed = skimage.util.crop(img, (y_bounds, x_bounds, c_bounds))50return bboxed51
52
53# This function is called but not used. It will trip on assert False if
54# the arguments are wrong (improper example)
55def verify_rescale(img, minsize):56# Here we use OpenCV transformation to match the C code57scale_amount = float(minsize) / min(img.shape[0], img.shape[1])58if scale_amount <= 1.0:59return img60
61print("Scale amount is %f -- should be < 1.0; got shape %s" %62(scale_amount, str(img.shape)))63assert False64img_cv = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)65output_shape = (int(np.ceil(scale_amount * img_cv.shape[0])),66int(np.ceil(scale_amount * img_cv.shape[1])))67resized = cv2.resize(img_cv,68dsize=output_shape,69interpolation=cv2.INTER_AREA)70
71resized = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)72assert resized.shape[0] >= minsize73assert resized.shape[1] >= minsize74return resized75
76
77def verify_crop(img, crop):78import skimage.util79assert img.shape[0] >= crop80assert img.shape[1] >= crop81y_offset = 082if img.shape[0] > crop:83y_offset = (img.shape[0] - crop) // 284
85x_offset = 086if img.shape[1] > crop:87x_offset = (img.shape[1] - crop) // 288
89y_bounds = (y_offset, img.shape[0] - crop - y_offset)90x_bounds = (x_offset, img.shape[1] - crop - x_offset)91c_bounds = (0, 0)92cropped = skimage.util.crop(img, (y_bounds, x_bounds, c_bounds))93assert cropped.shape[0] == crop94assert cropped.shape[1] == crop95return cropped96
97
98def verify_color_normalize(img, means, stds):99# Note the RGB/BGR inversion100# Operate on integers like the C version101img = img * 255.0102img[:, :, 0] = (img[:, :, 0] - means[2]) / stds[2]103img[:, :, 1] = (img[:, :, 1] - means[1]) / stds[1]104img[:, :, 2] = (img[:, :, 2] - means[0]) / stds[0]105return img * (1.0 / 255.0)106
107
108# Printing function (for debugging)
109def caffe2_img(img):110# Convert RGB to BGR111img = img[:, :, (2, 1, 0)]112# Convert HWC to CHW113img = img.swapaxes(1, 2).swapaxes(0, 1)114img = img * 255.0115return img.astype(np.int32)116
117
118# Bounding box is ymin, xmin, height, width
119def create_test(output_dir, width, height, default_bound, minsize, crop, means,120stds, count, label_type, num_labels, output1=None,121output2_size=None):122print("Creating a temporary lmdb database of %d pictures..." % (count))123
124if default_bound is None:125default_bound = [-1] * 4126
127LMDB_MAP_SIZE = 1 << 40128env = lmdb.open(output_dir, map_size=LMDB_MAP_SIZE, subdir=True)129index = 0130# Create images and the expected results131expected_results = []132with env.begin(write=True) as txn:133while index < count:134img_array = np.random.random_integers(1350, 255, [height, width, 3]).astype(np.uint8)136img_obj = Image.fromarray(img_array)137img_str = io.BytesIO()138img_obj.save(img_str, 'PNG')139
140# Create a random bounding box for every other image141# ymin, xmin, bound_height, bound_width142# TODO: To ensure that we never need to scale, we143# ensure that the bounding-box is larger than the144# minsize parameter145bounding_box = list(default_bound)146do_default_bound = True147if index % 2 == 0:148if height > minsize and width > minsize:149do_default_bound = False150bounding_box[0:2] = [np.random.randint(a) for a in151(height - minsize, width - minsize)]152bounding_box[2:4] = [np.random.randint(a) + minsize for a in153(height - bounding_box[0] - minsize + 1,154width - bounding_box[1] - minsize + 1)]155# print("Bounding box is %s" % (str(bounding_box)))156# Create expected result157img_expected = img_array.astype(np.float32) * (1.0 / 255.0)158# print("Orig image: %s" % (str(caffe2_img(img_expected))))159img_expected = verify_apply_bounding_box(160img_expected,161bounding_box)162# print("Bounded image: %s" % (str(caffe2_img(img_expected))))163
164img_expected = verify_rescale(img_expected, minsize)165
166img_expected = verify_crop(img_expected, crop)167# print("Crop image: %s" % (str(caffe2_img(img_expected))))168
169img_expected = verify_color_normalize(img_expected, means, stds)170# print("Color image: %s" % (str(caffe2_img(img_expected))))171
172tensor_protos = caffe2_pb2.TensorProtos()173image_tensor = tensor_protos.protos.add()174image_tensor.data_type = 4 # string data175image_tensor.string_data.append(img_str.getvalue())176img_str.close()177
178label_tensor = tensor_protos.protos.add()179label_tensor.data_type = 2 # int32 data180assert (label_type >= 0 and label_type <= 3)181if label_type == 0:182label_tensor.int32_data.append(index)183expected_label = index184elif label_type == 1:185binary_labels = np.random.randint(2, size=num_labels)186for idx, val in enumerate(binary_labels.tolist()):187if val == 1:188label_tensor.int32_data.append(idx)189expected_label = binary_labels190elif label_type == 2:191embedding_label = np.random.randint(100, size=num_labels)192for _idx, val in enumerate(embedding_label.tolist()):193label_tensor.int32_data.append(val)194expected_label = embedding_label195elif label_type == 3:196weight_tensor = tensor_protos.protos.add()197weight_tensor.data_type = 1 # float weights198binary_labels = np.random.randint(2, size=num_labels)199expected_label = np.zeros(num_labels).astype(np.float32)200for idx, val in enumerate(binary_labels.tolist()):201expected_label[idx] = val * idx202if val == 1:203label_tensor.int32_data.append(idx)204weight_tensor.float_data.append(idx)205
206if output1:207output1_tensor = tensor_protos.protos.add()208output1_tensor.data_type = 1 # float data209output1_tensor.float_data.append(output1)210
211output2 = []212if output2_size:213output2_tensor = tensor_protos.protos.add()214output2_tensor.data_type = 2 # int32 data215values = np.random.randint(1024, size=output2_size)216for val in values.tolist():217output2.append(val)218output2_tensor.int32_data.append(val)219
220expected_results.append(221[caffe2_img(img_expected), expected_label, output1, output2])222
223if not do_default_bound:224bounding_tensor = tensor_protos.protos.add()225bounding_tensor.data_type = 2 # int32 data226bounding_tensor.int32_data.extend(bounding_box)227
228txn.put(229'{}'.format(index).encode('ascii'),230tensor_protos.SerializeToString()231)232index = index + 1233# End while234# End with235return expected_results236
237
238def run_test(239size_tuple, means, stds, label_type, num_labels, is_test, scale_jitter_type,240color_jitter, color_lighting, dc, validator, output1=None, output2_size=None):241# TODO: Does not test on GPU and does not test use_gpu_transform242# WARNING: Using ModelHelper automatically does NHWC to NCHW243# transformation if needed.244width, height, minsize, crop = size_tuple245means = [float(m) for m in means]246stds = [float(s) for s in stds]247out_dir = tempfile.mkdtemp()248count_images = 2 # One with bounding box and one without249expected_images = create_test(250out_dir,251width=width,252height=height,253default_bound=(3, 5, height - 3, width - 5),254minsize=minsize,255crop=crop,256means=means,257stds=stds,258count=count_images,259label_type=label_type,260num_labels=num_labels,261output1=output1,262output2_size=output2_size263)264for device_option in dc:265with hu.temp_workspace():266reader_net = core.Net('reader')267reader_net.CreateDB(268[],269'DB',270db=out_dir,271db_type="lmdb"272)273workspace.RunNetOnce(reader_net)274outputs = ['data', 'label']275output_sizes = []276if output1:277outputs.append('output1')278output_sizes.append(1)279if output2_size:280outputs.append('output2')281output_sizes.append(output2_size)282imageop = core.CreateOperator(283'ImageInput',284['DB'],285outputs,286batch_size=count_images,287color=3,288minsize=minsize,289crop=crop,290is_test=is_test,291bounding_ymin=3,292bounding_xmin=5,293bounding_height=height - 3,294bounding_width=width - 5,295mean_per_channel=means,296std_per_channel=stds,297use_gpu_transform=(device_option.device_type == 1),298label_type=label_type,299num_labels=num_labels,300output_sizes=output_sizes,301scale_jitter_type=scale_jitter_type,302color_jitter=color_jitter,303color_lighting=color_lighting304)305
306imageop.device_option.CopyFrom(device_option)307main_net = core.Net('main')308main_net.Proto().op.extend([imageop])309workspace.RunNetOnce(main_net)310validator(expected_images, device_option, count_images)311# End for312# End with313# End for314shutil.rmtree(out_dir)315# end run_test
316
317
318@unittest.skipIf('cv2' not in sys.modules, 'python-opencv is not installed')319@unittest.skipIf('lmdb' not in sys.modules, 'python-lmdb is not installed')320class TestImport(hu.HypothesisTestCase):321def validate_image_and_label(322self, expected_images, device_option, count_images, label_type,323is_test, scale_jitter_type, color_jitter, color_lighting):324l = workspace.FetchBlob('label')325result = workspace.FetchBlob('data').astype(np.int32)326# If we don't use_gpu_transform, the output is in NHWC327# Our reference output is CHW so we swap328if device_option.device_type != 1:329expected = [img.swapaxes(0, 1).swapaxes(1, 2) for330(img, _, _, _) in expected_images]331else:332expected = [img for (img, _, _, _) in expected_images]333for i in range(count_images):334if label_type == 0:335self.assertEqual(l[i], expected_images[i][1])336else:337self.assertEqual(338(l[i] - expected_images[i][1] > 0).sum(), 0)339if is_test == 0:340# when traing data preparation is randomized (e.g. random cropping,341# Inception-style random sized cropping, color jittering,342# color lightin), we only compare blob shape343for (s1, s2) in zip(expected[i].shape, result[i].shape):344self.assertEqual(s1, s2)345else:346self.assertEqual((expected[i] - result[i] > 1).sum(), 0)347# End for348# end validate_image_and_label349
350@given(size_tuple=st.tuples(351st.integers(min_value=8, max_value=4096),352st.integers(min_value=8, max_value=4096)).flatmap(lambda t: st.tuples(353st.just(t[0]), st.just(t[1]),354st.just(min(t[0] - 6, t[1] - 4)),355st.integers(min_value=1, max_value=min(t[0] - 6, t[1] - 4)))),356means=st.tuples(st.integers(min_value=0, max_value=255),357st.integers(min_value=0, max_value=255),358st.integers(min_value=0, max_value=255)),359stds=st.tuples(st.floats(min_value=1, max_value=10),360st.floats(min_value=1, max_value=10),361st.floats(min_value=1, max_value=10)),362label_type=st.integers(0, 3),363num_labels=st.integers(min_value=8, max_value=4096),364is_test=st.integers(min_value=0, max_value=1),365scale_jitter_type=st.integers(min_value=0, max_value=1),366color_jitter=st.integers(min_value=0, max_value=1),367color_lighting=st.integers(min_value=0, max_value=1),368**hu.gcs)369@settings(verbosity=Verbosity.verbose, max_examples=10, deadline=None)370def test_imageinput(371self, size_tuple, means, stds, label_type,372num_labels, is_test, scale_jitter_type, color_jitter, color_lighting,373gc, dc):374def validator(expected_images, device_option, count_images):375self.validate_image_and_label(376expected_images, device_option, count_images, label_type,377is_test, scale_jitter_type, color_jitter, color_lighting)378# End validator379run_test(380size_tuple, means, stds, label_type, num_labels, is_test,381scale_jitter_type, color_jitter, color_lighting, dc, validator)382# End test_imageinput383
384@given(size_tuple=st.tuples(385st.integers(min_value=8, max_value=4096),386st.integers(min_value=8, max_value=4096)).flatmap(lambda t: st.tuples(387st.just(t[0]), st.just(t[1]),388st.just(min(t[0] - 6, t[1] - 4)),389st.integers(min_value=1, max_value=min(t[0] - 6, t[1] - 4)))),390means=st.tuples(st.integers(min_value=0, max_value=255),391st.integers(min_value=0, max_value=255),392st.integers(min_value=0, max_value=255)),393stds=st.tuples(st.floats(min_value=1, max_value=10),394st.floats(min_value=1, max_value=10),395st.floats(min_value=1, max_value=10)),396label_type=st.integers(0, 3),397num_labels=st.integers(min_value=8, max_value=4096),398is_test=st.integers(min_value=0, max_value=1),399scale_jitter_type=st.integers(min_value=0, max_value=1),400color_jitter=st.integers(min_value=0, max_value=1),401color_lighting=st.integers(min_value=0, max_value=1),402output1=st.floats(min_value=1, max_value=10),403output2_size=st.integers(min_value=2, max_value=10),404**hu.gcs)405@settings(verbosity=Verbosity.verbose, max_examples=10, deadline=None)406def test_imageinput_with_additional_outputs(407self, size_tuple, means, stds, label_type,408num_labels, is_test, scale_jitter_type, color_jitter, color_lighting,409output1, output2_size, gc, dc):410def validator(expected_images, device_option, count_images):411self.validate_image_and_label(412expected_images, device_option, count_images, label_type,413is_test, scale_jitter_type, color_jitter, color_lighting)414
415output1_result = workspace.FetchBlob('output1')416output2_result = workspace.FetchBlob('output2')417
418for i in range(count_images):419self.assertEqual(output1_result[i], expected_images[i][2])420self.assertEqual(421(output2_result[i] - expected_images[i][3] > 0).sum(), 0)422# End for423# End validator424run_test(425size_tuple, means, stds, label_type, num_labels, is_test,426scale_jitter_type, color_jitter, color_lighting, dc,427validator, output1, output2_size)428# End test_imageinput429
430
431if __name__ == '__main__':432import unittest433unittest.main()434