def test_distance_submatrix(self): """ Test distance computation. Test a submatrix is found inside a bigger matrix. """ img = np.array([[64, 2, 3, 61, 60, 6, 7, 57], [9, 55, 54, 12, 13, 51, 50, 16], [17, 47, 46, 20, 21, 43, 42, 24], [40, 26, 27, 37, 1, 1, 1, 33], [32, 34, 35, 29, 1, 1, 1, 25], [41, 23, 22, 44, 1, 1, 1, 48], [49, 15, 14, 52, 53, 11, 10, 56]]) img = np.uint8(gray2rgb(img)) patch = np.array([[26, 27, 37], [34, 35, 29], [23, 22, 44]]) patch = im2double(np.uint8(gray2rgb(patch))) q = Quilt(img, output_size=[20, 20]) result = q.distance(patch, tilesize=3, overlap=2, coord=[1, 1]) expected = zeros((5, 6)) self.assertEqual(expected.shape, result.shape) # check where the min is arg_min = np.where(result == np.min(result)) expected = np.asarray([[3], [1]]) assert_array_equal(expected, arg_min)
def test_show_list(self, mk_show): """ Test show calls _show method in Image just once with the right args. Test with a list of images as input. """ a = matrix2img(gray2rgb(np.array([[0, 0.5, 0.8], [1, 0.3, 0.7]]))) b = matrix2img(np.array([[0, 0.5, 0.8], [1, 0.3, 0.7], [1, 0.3, 0.7]])) c = matrix2img(gray2rgb(np.array([[0.5, 0.8], [0.3, 0.7], [0.3, 0.7]]))) show([a, b, c]) expected = [ mock.call(a, command=None, title=None), mock.call(b, command=None, title=None), mock.call(c, command=None, title=None) ] mk_show.assert_has_calls(expected)
def test_imresize_img_size(self): """ Test imresize result when a different size is required. """ img = np.asarray([[0., 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) img = Image.fromarray(gray2rgb(img), 'RGB') # bigger size = [5, 9] result = imresize(img, size) self.assertTrue(isinstance(result, Image.Image)) assert_array_equal(size[::-1], result.size) result = imresize(img, height=size[0], width=size[1]) assert_array_equal(size[::-1], result.size) # smaller size = [3, 2] result = imresize(img, size) assert_array_equal(size[::-1], result.size) self.assertTrue(isinstance(result, Image.Image)) result = imresize(img, height=size[0], width=size[1]) assert_array_equal(size[::-1], result.size) # other size = [10, 3] result = imresize(img, size) assert_array_equal(size[::-1], result.size) self.assertTrue(isinstance(result, Image.Image)) result = imresize(img, height=size[0], width=size[1]) assert_array_equal(size[::-1], result.size)
def test_imresize_3Dimg_base(self): """ Test imresize basic behaviour with input image as Image. Test the result is consistent if no scale is applied. """ img = np.asarray([[0., 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) img = Image.fromarray(gray2rgb(img), 'RGB') # scale result = imresize(img, scale=1) self.assertTrue(isinstance(result, Image.Image)) assert_array_equal(img, result) # size result = imresize(img, [4, 5]) self.assertTrue(isinstance(result, Image.Image)) assert_array_equal(img, result) # height only result = imresize(img, height=4) self.assertTrue(isinstance(result, Image.Image)) assert_array_equal(img, result) # width only result = imresize(img, width=5) self.assertTrue(isinstance(result, Image.Image)) assert_array_equal(img, result) # height and width result = imresize(img, height=4, width=5) self.assertTrue(isinstance(result, Image.Image)) assert_array_equal(img, result)
def test_imresize_3Dmatrix_base(self): """ Test imresize basic behaviour with input image as matrix. Test the result is consistent if no scale is applied. """ img = np.asarray([[0., 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) img = gray2rgb(img) # scale result = np.around(imresize(img, scale=1), decimals=1) self.assertTrue(isinstance(result, np.ndarray)) assert_array_equal(img, result) # size result = np.around(imresize(img, [4, 5]), decimals=1) self.assertTrue(isinstance(result, np.ndarray)) assert_array_equal(img, result) # height only result = np.around(imresize(img, height=4), decimals=1) self.assertTrue(isinstance(result, np.ndarray)) assert_array_equal(img, result) # width only result = np.around(imresize(img, width=5), decimals=1) self.assertTrue(isinstance(result, np.ndarray)) assert_array_equal(img, result) # height and width result = np.around(imresize(img, height=4, width=5), decimals=1) self.assertTrue(isinstance(result, np.ndarray)) assert_array_equal(img, result)
def test_distance_candidates(self): """ Test that all the candidates are good choices. """ # eye matrix img = -np.eye(10) * 2 + 1 img = np.asarray(np.dstack((img, img, img))) # parch is similar to a submatrix of img patch = np.asarray([[1, 1, 1, 1.], [1, 1, 1, 1.], [-1, 1, 0, 0.], [1, -1, 0, 0.]]) patch = im2double(gray2rgb(patch)) # compute distance q = Quilt(img, output_size=[20, 20]) distances = q.distance(patch, tilesize=4, overlap=2, coord=[2, 2]) best = np.min(distances) candidates = np.where(distances <= best) # submatrix of img similar to patch expected = np.asarray([[1, 1, 1, 1], [1, 1, 1, 1], [-1, 1, 1, 1], [1, -1, 1, 1]]) for i in range(len(candidates[0])): sub = [candidates[0][i], candidates[1][i]] result = img[sub[0]:sub[0] + 4, sub[1]:sub[1] + 4, 1] assert_array_equal(expected, result)
def test_show_single_uint(self, mk_show): """ Test show calls _show method in Image just once. Test with a single uint8 image as input. """ a = np.array([[0, 0.5, 0.8], [1, 0.3, 0.7]]) show(np.uint8(gray2rgb(a))) mk_show.assert_called_once_with(mock.ANY, command=None, title=None)
def test_show_single_float(self, mk_show): """ Test show calls _show method in Image just once with the right args. Test with a single float64 image as input. """ a = np.array([[0, 0.5, 0.8], [1, 0.3, 0.7]]) a = matrix2img(gray2rgb(a)) show(a) mk_show.assert_called_once_with(a, command=None, title=None)
def test_save_single_matrix(self, mk_save): """ Test save calls the save() method of each input images with the realtive paths. """ a = gray2rgb(np.array([[0, 0.5, 0.8], [1, 0.3, 0.7]])) path = 'custom/path/a.jpg' save(a, path) mk_save.assert_called_once_with(path)
def test_gray2rgb_rgb(self): """ Test gray2rgb behaviour when given a 3-channels matrix. """ a = np.array([[[0, 0, 0], [9, 9, 9], [8, 8, 8]], [[8, 8, 8], [7, 7, 7], [9, 9, 9]]]) result = gray2rgb(a) expected = a assert_array_equal(expected, result)
def test_save_list_img(self): """ Test save calls the save() method of each input images with the realtive paths. """ a = matrix2img(gray2rgb(np.array([[0, 0.5, 0.8], [1, 0.3, 0.7]]))) b = matrix2img(np.array([[0, 0.5, 0.8], [1, 0.3, 0.7], [1, 0.3, 0.7]])) c = matrix2img(gray2rgb(np.array([[0.5, 0.8], [0.3, 0.7], [0.3, 0.7]]))) a_path = 'custom/path/a.jpg' b_path = 'custom/path/b.jpg' c_path = 'custom/path/c.jpg' with mock.patch.object(a, 'save') as mka: with mock.patch.object(b, 'save') as mkb: with mock.patch.object(c, 'save') as mkc: save([a, b, c], [a_path, b_path, c_path]) mka.assert_called_once_with(a_path) mkb.assert_called_once_with(b_path) mkc.assert_called_once_with(c_path)
def create_flip(cls, img, amount=(0, 0)): """ Generates the required flips of the input image and builds an image containing the input image and its flips. The final image is so composed: _______ If amount = [0, 1]: | img | |_______| | flipV | |_______| _______ _______ If amount = [1, 0]: | img | flipH | |_______|_______| _______ _______ If amount = [1, 1]: | img | flipH | |_______|_______| | flipV |flipHV | |_______|_______| Args: img: image to be rotated amount: Returns: image composed of the input one and it rotations """ # check the amount if not amount or amount == (0, 0): return img # turn the input image into a 3-channel image third_dim = len(img.shape) == 3 img = gray2rgb(img) # vertical if amount[0]: flip = zeros((img.shape[0] * 2 + 1, img.shape[1], 3)) flip[:img.shape[0], :, :] = img flip[img.shape[0] + 1:, :, :] = flipud(img) img = flip # horizontal if amount[1]: flip = zeros((img.shape[0], img.shape[1] * 2 + 1, 3)) flip[:, :img.shape[1], :] = img flip[:, img.shape[1] + 1:, :] = fliplr(img) img = flip # if the input image had 1 channel only, also the result will do if not third_dim: img = img[:, :, 0] show(img) if cls.debug else None return img
def test_filter_img(self): """ Test filter_img. Given two images and a mask, test the result is correct """ a = np.array([[30, 39, 48, 1], [38, 47, 7, 9], [46, 6, 8, 17], [5, 14, 16, 25]]) a = gray2rgb(a) b = np.array([[9, 18, 27, 29], [17, 26, 35, 37], [25, 34, 36, 45], [33, 42, 44, 4]]) b = gray2rgb(b) m = np.array([[0, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1], [0, 0, 0, 1]]) result = filter_img(a, b, m) expected = np.asarray([[9, 18, 48, 1], [17, 47, 7, 9], [46, 6, 8, 17], [33, 42, 44, 25]]) expected = gray2rgb(expected) # size self.assertEqual(expected.shape, result.shape) # values np.testing.assert_array_equal(expected, result)
def test_save_single_img(self): """ Test save calls the save() method of the input image once and with the given path. """ a = matrix2img(gray2rgb(np.array([[0, 0.5, 0.8], [1, 0.3, 0.7]]))) path = 'custom/path' with mock.patch.object(a, 'save') as mk_save: save(a, path) mk_save.assert_called_once_with(path)
def test_img2matrix_3Dmatrix(self): """ Test img2matrix with a 3d matrix as input. """ matrix = np.asarray([[0., 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) matrix = gray2rgb(matrix) result = img2matrix(matrix) # type self.assertTrue(isinstance(result, np.ndarray)) # dimensions assert_array_equal(matrix.shape, result.shape) # values assert_array_equal(matrix, result)
def test_matrix2img_3Dimg(self): """ Test matrix2img with an RGB image as input. """ matrix = np.asarray([[0., 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) img = Image.fromarray(gray2rgb(matrix), 'RGB') result = matrix2img(img) # type self.assertTrue(isinstance(result, Image.Image)) # dimensions assert_array_equal(img.size, result.size) # values assert_array_equal(img, result)
def test_matrix2img_3Dmatrix(self): """ Test matrix2img with a 3d matrix as input. """ matrix = np.asarray([[0., 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) matrix = gray2rgb(matrix) result = matrix2img(matrix) # type self.assertTrue(isinstance(result, Image.Image)) # dimensions assert_array_equal(matrix.shape[0:2][::-1], result.size) # values expected = np.uint8(matrix * 255) assert_array_equal(expected, np.asarray(result))
def test_img2matrix_3Dimg(self): """ Test img2matrix with an RGB image as input. """ matrix = np.asarray([[.3, 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) matrix = gray2rgb(matrix) img = matrix2img(matrix) result = img2matrix(img) # type self.assertTrue(isinstance(result, np.ndarray)) # dimensions assert_array_equal(matrix.shape, result.shape) # values assert_array_equal(matrix, np.around(result, decimals=1))
def test_src_size(self): """ Test the source image is turned to rgb float and is not reshaped. """ q = Quilt(self.x, output_size=self.x.shape) # there is just one image in the stack self.assertEqual(1, len(q.X)) # rgb assert_array_equal((self.x.shape[0], self.x.shape[1], 3), q.X[0].shape) # float self.assertEqual('float', q.X[0].dtype) expected = gray2rgb(im2double(self.x)) assert_array_equal(expected, q.X[0])
def test_imresize_img_scale(self): """ Test imresize result when a different scale is required. """ img = np.asarray([[0., 1., .2, .3, .4], [.5, .8, .7, .8, .9], [1., .0, .2, .3, .4], [.5, .7, .9, .9, .8]]) img = Image.fromarray(gray2rgb(img), 'RGB') # bigger result = imresize(img, scale=1.4) self.assertTrue(isinstance(result, Image.Image)) expected_size = (7, 5) assert_array_equal(expected_size, result.size) # smaller result = imresize(img, scale=0.8) self.assertTrue(isinstance(result, Image.Image)) expected_size = (4, 3) assert_array_equal(expected_size, result.size)
def test_gray2rgb_gray(self): """ Test gray2rgb behaviour when given a mono-channel matrix. """ a = np.array([[0, 9, 8], [8, 7, 9]]) result = gray2rgb(a) # size expected_size = (a.shape[0], a.shape[1], 3) assert_array_equal(expected_size, result.shape) # values are from input expected_values = np.unique(a) assert_array_equal(expected_values, np.unique(result)) # test with matrix expected = np.array([[[0, 0, 0], [9, 9, 9], [8, 8, 8]], [[8, 8, 8], [7, 7, 7], [9, 9, 9]]]) assert_array_equal(expected, result)
def _set_src(self, img, rotate=0, flip=None): """ Manages source image/s: - turns it into a stack of images of the same size - images are set to float values in range [0, 1] - rotated images are added if required Args: img: source image/s: can be a single image or a stack of images of the same size rotate: number of 90 degrees rotations to apply to each image in the stack. flip: list of two booleans for [flip_vertical, flip_horizontal] Returns: - reference image (the first image of the stack with no rotations) - stack of the source images """ # stack of images if not isinstance(img, list): img = [img] reference = None for i in xrange(len(img)): # images in the stack must have the same size if i and not img[i].shape[0:2] == img[0].shape[0:2]: raise ValueError('Chained images must have the same size. Got ' '{0} and {1}'.format(img[i].shape[0:2], img[0].shape[0:2])) # 3 channel images img[i] = gray2rgb(img[i]) # float values in range [0, 1] img[i] = im2double(img[i]) if not i: reference = img[i] # rotation img = [self.create_rotations(i, rotate) for i in img] img = [self.create_flip(i, flip) for i in img] return reference, img
def test_stack(self): """ Test the the source images are edited consistently and a same number of destination images is prepared. """ q = Quilt([self.x, self.x, self.x], output_size=[30, 20]) # there are 3 images in the stacks expected_x = gray2rgb(im2double(self.x)) expected_y = zeros((30, 20, 3)) self.assertEqual(3, len(q.X)) self.assertEqual(3, len(q.Y)) for i in xrange(3): # src assert_array_equal((self.x.shape[0], self.x.shape[1], 3), q.X[i].shape) self.assertEqual('float', q.X[i].dtype) assert_array_equal(expected_x, q.X[i]) # dst assert_array_equal((30, 20, 3), q.Y[i].shape) assert_array_equal(expected_y, q.Y[i])
def optimized_compute(self): """ First process: it computes the quilt algorithm with big tiles, manages child processes and them combines the results. 1) creates the child processes (number defined according to the available cores and the number of big tiles in the image) 2) computes quilting with big tiles 3) every time a tile is computed (and sewed with the image), it is put in a queue process 1: big tiles for each of the tile: process n """ self.log.info('\nMULTIPROCESSING COMPUTING ...') big_num_tiles = self.calc_num_tiles(tile_size=self.big_tilesize, overlap=self.big_overlap) # prepare the pool n_proc = min(big_num_tiles[0] * big_num_tiles[1], self.cores) out_queue = Queue() in_queue = JoinableQueue() pool = Pool(n_proc, unwrap_self, ( self, in_queue, out_queue, )) self.log.info('preparing {0} processes - {1}'.format( n_proc, time.strftime("%H:%M:%S"))) if self.Ymask is not None: # zero values will become inf Ymask_rgb = gray2rgb(self.Ymask) # use the mask as a draft of the dst img so that boundaries are # respected self.Y[0] = deepcopy(Ymask_rgb) for i in xrange(big_num_tiles[0]): startI = i * self.big_tilesize - i * self.big_overlap endI = min(self.Y[0].shape[0], startI + self.big_tilesize) sizeI = endI - startI if sizeI <= self.overlap: continue for j in xrange(big_num_tiles[1]): startJ = j * self.big_tilesize - j * self.big_overlap endJ = min(self.Y[0].shape[1], startJ + self.big_tilesize) sizeJ = endJ - startJ if sizeJ <= self.overlap: continue dst_patches = [y[startI:endI, startJ:endJ, :] for y in self.Y] # for the big tiles don't consider the mask, since it would # remove most of the image because the tiles are so big res_patches = self._compute_patch( dst_patches, [sizeI, sizeJ], (i, j), mask=self.Xmask_big, constraint_start=self.constraint_start, err=0.8) # add the mask on top if self.Ymask is not None: res_patches = [ r * Ymask_rgb[startI:endI, startJ:endJ] for r in res_patches ] for idx, res in enumerate(res_patches): self.Y[idx][startI:endI, startJ:endJ, :] = res # make a process start in this big tile _img = [y[startI:endI, startJ:endJ, :] for y in self.Y] _mask = self.Ymask[startI:endI, startJ:endJ] \ if self.Ymask is not None else None _id = (startI, startJ) in_queue.put({'dst': _img, 'mask': _mask, 'id': _id}) # wait for all the children self.log.debug('master finished {0}'.format(time.strftime("%H:%M:%S"))) show(self.Y[0]) if self.debug else None pool.close() self.log.debug('closed, in queue: {0} out: {1}'.format( in_queue.qsize(), out_queue.qsize())) in_queue.join() self.log.debug('all children finished {0}'.format( time.strftime("%H:%M:%S"))) # get the results results = sorted([ out_queue.get() for _ in xrange(big_num_tiles[0] * big_num_tiles[1]) ]) # sew them together for idx, res in results: # calculate the mask base_patch = self.Y[0][idx[0]:idx[0] + self.big_tilesize, idx[1]:idx[1] + self.big_tilesize] new_patch = res[0] mask_patch = self.calc_patch_mask(base_patch, new_patch, coord=idx, overlap=self.big_overlap) # apply the mask to each layer for i, y in enumerate(self.Y): base_patch = y[idx[0]:idx[0] + self.big_tilesize, idx[1]:idx[1] + self.big_tilesize] new_patch = res[i] self.Y[i][idx[0]:idx[0]+self.big_tilesize, idx[1]:idx[1]+self.big_tilesize, :] = \ filter_img(new_patch, base_patch, mask_patch) # apply the mask again if self.Ymask is not None: self.Y = [r * Ymask_rgb for r in self.Y] show(self.Y[0]) if self.debug else None if self.result_path: save(self.Y[0], self.result_path) self.log.info('saving' + self.result_path)
def create_rotations(cls, img, amount): """ Generates the required rotations of the input image and builds an image containing the input image and its rotations (the remaining space is left zero). The final image is so composed: _______ If amount == 2: | rot0 | |_______| |rot180 | |_______| _______ _____ _____ If amount == 4: | rot0 | | | |_______| rot | rot | |rot180 | 90 | -90 | |_______|_____|_____| Args: img: image to be rotated amount: amount of rotation of 90 degrees. E.g.: amount = 2 --> rotation = 90*2 = 180 Accepted values: {0, 2, 4} Returns: image composed of the input one and it rotations """ # check the amount if not amount: return img if amount not in [2, 4]: raise ValueError( 'Rotation must be None, 2 or 4. Got {0}'.format(amount)) # turn the input image into a 3-channel image third_dim = len(img.shape) == 3 img = gray2rgb(img) rot = None if amount == 2: rot = zeros((img.shape[0] * 2 + 1, img.shape[1], 3)) rot[:img.shape[0], :, :] = img rot[img.shape[0] + 1:, :, :] = rot90(img, 2) if amount == 4: # set so that height > width if img.shape[0] > img.shape[1]: img = rot90(img, 1) rot = zeros((max(img.shape[1], img.shape[0] * 2 + 1), img.shape[1] + img.shape[0] * 2 + 2, 3)) rot[:img.shape[0], :img.shape[1], :] = img rot[img.shape[0] + 1: img.shape[0] * 2 + 1, :img.shape[1], :] = \ rot90(img, 2) rot[:img.shape[1], img.shape[1] + 1:img.shape[1] + 1 + img.shape[0], :] = rot90( img, 1) rot[:img.shape[1], img.shape[0] + img.shape[1] + 2:, :] = \ rot90(img, 3) # if the input image had 1 channel only, also the result will do if not third_dim: rot = rot[:, :, 0] show(rot) if cls.debug else None return rot