def __init__(self, intrinsics, extrinsics, distortion=None, camera_id=None, warning=True, debug=True): self.intrinsics = intrinsics self.extrinsics = extrinsics if distortion is not None: # to make it full self.distortion = distortion if camera_id is not None: assert isstring(camera_id), 'the camera id is a string' self.camera_id = camera_id if debug: assert isnparray(self.intrinsics) and self.intrinsics.shape == (3, 3), 'the intrinsics is not correct' assert isnparray(self.extrinsics) and self.extrinsics.shape == (3, 4), 'the extrinsics is not correct'
def apply_rotation_tight(bbox_in, angle_in_degree, im_shape, debug=True): ''' return 4 points clockwise ''' if debug: assert isnparray(bbox_in) and bbox_in.size == 4, 'box is not correct' bbox_in = np.reshape(bbox_in, (4, )) bbox_tight = bbox_rotation_inv(bbox_in, angle_in_degree, im_shape, debug=debug) # get top left and bottom right coordinate of the rotated bbox in the image coordinate # print('bbox after inverse the rotation') # print(bbox_tight) pts_total = np.zeros((4, 2), dtype=np.int) pts_tl = np.array([bbox_tight[0], bbox_tight[1]]) pts_br = np.array([bbox_tight[2], bbox_tight[3]]) line1 = get_2dline_from_pts_slope(imagecoor2cartesian(pts_tl, debug=debug), angle_in_degree + 90.00, debug=debug) line2 = get_2dline_from_pts_slope(imagecoor2cartesian(pts_br, debug=debug), angle_in_degree, debug=debug) pts_bl = cartesian2imagecoor(get_2dpts_from_lines(line1, line2, debug=debug), debug=debug) pts_tr = cartesian2imagecoor(get_2dpts_from_lines(get_2dline_from_pts_slope(imagecoor2cartesian(pts_tl, debug=debug), angle_in_degree, debug=debug), get_2dline_from_pts_slope(imagecoor2cartesian(pts_br, debug=debug), angle_in_degree + 90.00, debug=debug), debug=debug), debug=debug) # print np.reshape(pts_tl, (1, 2)).shape # print pts_total[0, :].shape pts_total[0, :] = np.reshape(pts_tl, (1, 2)) pts_total[1, :] = np.reshape(pts_tr, (1, 2)) pts_total[2, :] = np.reshape(pts_br, (1, 2)) pts_total[3, :] = np.reshape(pts_bl, (1, 2)) return pts_total
def bbox_rotatedtight2rotatedloose(bbox_in, angle_in_degree, debug=True): ''' transfer the rotated bbox with tight version to loose version, both contains only two points (top left and bottom right) only a single box is feeded into ''' if debug: assert isnparray(bbox_in) and bbox_in.size == 4, 'box is not correct' pts_tl = np.array([bbox_in[0], bbox_in[1]]) pts_br = np.array([bbox_in[2], bbox_in[3]]) line1 = get_2Dline_from_pts_slope(imagecoor2cartesian(pts_tl), angle_in_degree + 90.00) line2 = get_2Dline_from_pts_slope(imagecoor2cartesian(pts_br), angle_in_degree) pts_bl = cartesian2imagecoor(get_2dpts_from_lines(line1, line2)) pts_tr = cartesian2imagecoor( get_2dpts_from_lines( get_2Dline_from_pts_slope(imagecoor2cartesian(pts_tl), angle_in_degree), get_2Dline_from_pts_slope(imagecoor2cartesian(pts_br), angle_in_degree + 90.00))) # assert_almost_equal(np.dot(pts_bl - pts_br, pts_bl - pts_tl), 0, err_msg='The intersection points are wrong') # assert_almost_equal(np.dot(pts_tr - pts_br, pts_tr - pts_tl), 0, err_msg='The intersection points are wrong') pts_tl_final = np.zeros((2), dtype=np.float32) pts_br_final = np.zeros((2), dtype=np.float32) pts_tl_final[0] = min({pts_tl[0], pts_br[0], pts_bl[0], pts_tr[0]}) pts_tl_final[1] = min({pts_tl[1], pts_br[1], pts_bl[1], pts_tr[1]}) pts_br_final[0] = max({pts_tl[0], pts_br[0], pts_bl[0], pts_tr[0]}) pts_br_final[1] = max({pts_tl[1], pts_br[1], pts_bl[1], pts_tr[1]}) # print(pts_tl_final) # print(pts_br_final) test = np.hstack((pts_tl_final, pts_br_final)) return test
def image_chw2hwc(input_image, warning=True, debug=True): ''' this function transpose the channels of an image from CHW to HWC parameters: input_image: a numpy CHW image outputs: np_image: a numpy HWC image ''' if debug: isnparray(input_image), 'the input image is not a numpy' np_image = input_image.copy() if debug: assert np_image.ndim == 3 and np_image.shape[0] == 3, 'the input numpy image does not have a good dimension: {}'.format(np_image.shape) return np.transpose(np_image, (1, 2, 0))
def safe_angle(input_angle, radian=False, warning=True, debug=True): ''' make ensure the rotation is in [-180, 180] in degree parameters: input_angle: an angle which is supposed to be in degree radian: if True, the unit is replaced to radian instead of degree outputs: angle: an angle in degree within (-180, 180] ''' angle = copy.copy(input_angle) if debug: assert isscalar(angle), 'the input angle should be a scalar' if isnparray(angle): angle = angle[0] # single numpy scalar value if radian: while angle > np.pi: angle -= np.pi while angle <= -np.pi: angle += np.pi else: while angle > 180: angle -= 360 while angle <= -180: angle += 360 return angle
def safe_image_like(input_image, warning=True, debug=True): ''' return an image-like numpy no matter what format the input is, the numpy has the image shape, but value may not be in [0, 1] for float image make sure the output numpy image is a copy of the input image note: an image-like numpy array is an array with image-like shape, but might contain arbitrary value parameters: input_image: pil image or image-like array, color or gray, float or uint outputs: np_image: numpy image, with the same color and datatype as the input isnan: return True if any nan value exists ''' if ispilimage(input_image): np_image = np.array(input_image) elif isnparray(input_image): np_image = input_image.copy() assert isimage_dimension(np_image), 'the input is not an image-like numpy array' else: assert False, 'only pil and numpy image-like data are supported' isnan = isnannparray(np_image) if warning and isnan: print('nan exists in the image data') return np_image, isnan
def hist_equalization(input_data, num_bins=256, warning=True, debug=True): ''' convert a N-d numpy data (or list) with random distribution to a 1-d data with equalized histogram e.g., for the samples from a gaussian distribution, the data points are dense in the middle, the cdf increases fast in the middle so that the discrete cdf is sparse in the middle, the equalized data points are interpolated from cdf such that the density can be the same for the middle and the rest parameters: input_data: a list or a numpy data, could be any shape, not necessarily a 1-d data, can be integer data (uint8 image) or float data (float32 image) num_bins: bigger, the histogram of equalized data points is more flat outputs: data_equalized: equalized data with the same shape as input, it is float with [0, 1] ''' np_data = safe_npdata(input_data, warning=warning, debug=debug) if debug: assert isnparray(np_data), 'the input data is not a numpy data' ori_shape = np_data.shape np_data = np_data.flatten() hist, xs = np.histogram( np_data, num_bins, density=True) # return distribution and X's coordinates cdf = hist.cumsum() cdf = cdf / cdf[-1] # sparse in the middle data_equalized = np.interp(np_data, xs[:-1], cdf) return data_equalized.reshape((ori_shape))
def safe_batch_deep_image(input_image, warning=True, debug=True): ''' return a batch image-like deep numpy no matter what format the input is, the shape of input should be N3HW or 3HW, make sure the output numpy image is a copy of the input image note: an image-like numpy array is an array with image-like shape, but might contain arbitrary value parameters: input_image: image-like numpy array, N3HW or 3HW, float or uint outputs: np_image: N3HW numpy image, with the same datatype as the input isnan: return True if any nan value exists ''' if debug: assert isnparray(input_image), 'the input image should be a numpy array' np_image = input_image.copy() # if np_image.ndim == 2: # expand HW gradscale image to CHW image with one channel # np_image = np.expand_dims(np_image, axis=0) if np_image.ndim == 3: # expand CHW to NCHW batch images with batch of 1 if debug: assert np_image.shape[0] == 3, 'the image should be a color image' np_image = np.expand_dims(np_image, axis=0) elif np_image.ndim == 4: if debug: assert np_image.shape[1] == 3, 'the image should be a color image' else: assert False, 'only color images are supported' isnan = isnannparray(np_image) if warning and isnan: print('nan exists in the image data') return np_image, isnan
def generate_list_from_data(save_path, src_data, debug=True): ''' generate a file which contains a 1-d numpy array data parameter: src_data: a list of 1 element data, or a 1-d numpy array data ''' save_path = safepath(save_path) if debug: if isnparray(src_data): assert src_data.ndim == 1, 'source data is incorrect' elif islist(src_data): assert all(np.array(data_tmp).size == 1 for data_tmp in src_data), 'source data is in correct' assert isfolder(save_path) or isfile( save_path), 'save path is not correct' if isfolder(save_path): save_path = os.path.join(save_path, 'datalist.txt') if debug: assert is_path_exists_or_creatable( save_path), 'the file cannot be created' with open(save_path, 'w') as file: for item in src_data: file.write('%f\n' % item) file.close()
def identity(data, data_range=None, debug=True): if debug: print( 'debug mode is on during identity function. Please turn off after debuging' ) assert isnparray(data), 'data is not correct' return data
def data_normalize(input_data, method='max', data_range=None, sum=1, warning=True, debug=True): ''' this function normalizes N-d data in different ways: 1) normalize the data from a range to [0, 1]; 2) normalize the data which sums to a value parameters: input_data: a list or a numpy N-d data to normalize method: max: normalize the data from a range to [0, 1], when the range is not given, the max and min are obtained from the data sum: normalize the data such that all elements are summed to a value, the default value is 1 data_range: None or 2-element tuple, list or array sum: a scalar outputs: normalized_data: a float32 numpy array with same shape as the input data ''' np_data = safe_npdata(input_data, warning=warning, debug=debug).astype('float32') if debug: assert isnparray(np_data), 'the input data is not a numpy data' assert method in ['max', 'sum'], 'the method for normalization is not correct' if method == 'max': if data_range is None: max_value, min_value = np.max(np_data), np.min(np_data) else: if debug: assert isrange(data_range), 'data range is not correct' max_value, min_value = data_range[1], data_range[0] elif method == 'sum': if debug: assert isscalar(sum), 'the sum is not correct' max_value, min_value = np.sum(np_data) / sum, 0 normalized_data = (np_data - min_value) / (max_value - min_value) # normalization return normalized_data
def safe_batch_image(input_image, warning=True, debug=True): ''' return a numpy image no matter what format the input is make sure the output numpy image is a copy of the input image parameters: input_image: a numpy image, NHWC, float or uint outputs: np_image: NHWC numpy image, with the same datatype as the input isnan: return True if any nan value exists ''' if debug: assert isnparray(input_image), 'the input image should be a numpy array' np_image = input_image.copy() if np_image.ndim == 2: np_image = np.expand_dims(np_image, axis=0) # compatible with grayscale image if np_image.ndim == 3: # expand HWC to NHWC batch images with batch of 1 if debug: assert isnpimage(np_image), 'the image should be a numpy image' np_image = np.expand_dims(np_image, axis=0) elif np_image.ndim == 4: if debug: assert np_image.shape[-1] == 3 or np_image.shape[-1] == 1, 'the image shape is not good' for image_index in range(np_image.shape[0]): assert isnpimage(np_image[image_index]), 'each individual image should be a numpy image' else: assert False, 'only color images are supported' isnan = isnannparray(np_image) if warning and isnan: print('nan exists in the image data') return np_image, isnan
def nparray_chw2hwc(input_nparray, warning=True, debug=True): ''' this function transpose the channels of an numpy array from CHW to HWC parameters: input_nparray: a numpy CHW array outputs: np_array: a numpy HWC array ''' if debug: isnparray(input_nparray), 'the input array is not a numpy' np_array = input_nparray.copy() if debug: assert np_array.ndim == 3, 'the input numpy array does not have a good dimension: {}'.format( np_image.shape) return np.transpose(np_array, (1, 2, 0))
def save_2dmatrix_to_file(data, save_path, formatting='%.1f', debug=True): save_path = safepath(save_path) if debug: assert isnparray(data) and len( data.shape) == 2, 'input data is not 2d numpy array' assert is_path_exists_or_creatable( save_path), 'save path is not correct' mkdir_if_missing(save_path) # assert isnparray(data) and len(data.shape) <= 2, 'the data is not correct' np.savetxt(save_path, data, delimiter=' ', fmt=formatting)
def cartesian2imagecoor(pts, debug=True): ''' change the coordinate system from normal cartesian system back to image coordinate system, basically reverse the y coordinate parameter: pts: a single point (list, tuple, numpy array) or a 2 x N numpy array representing a set of points return: pts: a tuple if only single point comes in or a 2 x N numpy array ''' if debug: assert is2dpts(pts) or (isnparray(pts) and pts.shape[0] == 2 and pts.shape[1] > 0), 'point is not correct' if is2dpts(pts): if isnparray(pts): pts = np.reshape(pts, (2, )) return (pts[0], -pts[1]) else: pts[1, :] = -pts[1, :] return pts
def safe_2dptsarray(input_pts, homogeneous=False, dimen_add=0, warning=True, debug=True): ''' make sure to copy the pts array without modifying it and make the dimension to 2(3 if homogenous) x N parameters: input_pts: a list of 2(3 if homogenous) elements, a listoflist of 2 elements: e.g., [[1,2], [5,6]], a numpy array with shape or (2, N) or (2, ) homogeneous: the input points are in the homogenous coordinate dimen_add: additional dimension, used to accommdate for higher dimensional array outputs: np_pts: 2 (3 if homogenous) X N numpy array ''' if homogeneous: dimension = 3 + dimen_add else: dimension = 2 + dimen_add if islist(input_pts): if islistoflist(input_pts): if debug: assert all( len(list_tmp) == dimension for list_tmp in input_pts ), 'all sub-lists should have length of %d' % dimension np_pts = np.array(input_pts).transpose() else: if debug: assert len( input_pts ) == dimension, 'the input pts list does not have a good shape' np_pts = np.array(input_pts).reshape((dimension, 1)) elif isnparray(input_pts): input_pts = input_pts.copy() if input_pts.shape == (dimension, ): np_pts = input_pts.reshape((dimension, 1)) else: np_pts = input_pts else: assert False, 'only list and numpy array for pts are supported' if debug: if homogeneous: assert is2dptsarray_homogeneous( np_pts), 'the input pts array does not have a good shape' else: assert is2dptsarray( np_pts), 'the input pts array does not have a good shape' return np_pts
def pol2cart_2d_degree(pts, debug=True): ''' input point: (rho, phi) phi is in degree ''' if debug: assert istuple(pts) or islist(pts) or isnparray( pts), 'input point is not correct' assert np.array(pts).size == 2, 'input point is not 2d points' rho = pts[0] phi = math.radians(pts[1]) x = rho * np.cos(phi) y = rho * np.sin(phi) return (x, y)
def safe_npdata(input_data, warning=True, debug=True): ''' copy a list of data or a numpy data to the buffer for use parameters: input_data: a list, a scalar or numpy data outputs: np_data: a copy of numpy data ''' if islist(input_data): np_data = np.array(input_data) elif isscalar(input_data): np_data = np.array(input_data).reshape((1, )) elif isnparray(input_data): np_data = input_data.copy() else: assert False, 'only list of data, scalar or numpy data are supported' return np_data
def data_unnormalize(data, data_range, debug=True): ''' this function unnormalizes 1-d label to normal scale based on range of data ''' np_data = safe_npdata(input_data, warning=warning, debug=debug).astype('float32') if debug: assert isnparray(np_data), 'the input data is not a numpy data' assert isrange(data_range), 'data range is not correct' max_value = data_range[1] min_value = data_range[0] unnormalized = np_data * (max_value - min_value) + min_value # if debug: # normalized = normalize_data(data=unnormalized, data_range=data_range, debug=False) # assert_almost_equal(data, normalized, decimal=6, err_msg='data is not correct: %f vs %f' % (data, normalized)) return unnormalized
def safe_center_bbox(input_bbox, warning=True, debug=True): ''' make sure to copy the center bbox without modifying it and make the dimension to N x 4 or N x 2 parameters: input_bbox: a list of 4 (2) elements, a listoflist of 4 (2) elements: e.g., [[1,2,3,4], [5,6,7,8]], a numpy array with shape or (N, 4) or (4, ) or (N, 2) or (2, ) outputs: np_bboxes: N X 4 (2) numpy array ''' if islist(input_bbox): if islistoflist(input_bbox): if debug: assert all( len(list_tmp) == 4 or len(list_tmp) == 2 for list_tmp in input_bbox), 'all sub-lists should have length of 4' np_bboxes = np.array(input_bbox) else: if debug: assert len(input_bbox) == 4 or len( input_bbox ) == 2, 'the center bboxes list does not have a good shape' if len(input_bbox) == 4: np_bboxes = np.array(input_bbox).reshape((1, 4)) else: np_bboxes = np.array(input_bbox).reshape((1, 2)) elif isnparray(input_bbox): input_bbox = input_bbox.copy() if input_bbox.shape == (4, ): np_bboxes = input_bbox.reshape((1, 4)) elif input_bbox.shape == (2, ): np_bboxes = input_bbox.reshape((1, 2)) else: if debug: assert iscenterbbox( input_bbox ), 'the input center bbox numpy array does not have a good shape' np_bboxes = input_bbox else: assert False, 'only list and numpy array for bbox are supported' return np_bboxes
def safe_angle(input_angle, warning=True, debug=True): ''' make ensure the rotation is in [-180, 180] in degree parameters: input_angle: an angle which is supposed to be in degree outputs: angle: an angle in degree within (-180, 180] ''' angle = copy.copy(input_angle) if debug: assert isscalar(angle), 'the input angle should be a scalar' if isnparray(angle): angle = angle[0] # single numpy scalar value while angle > 180: angle -= 360 while angle <= -180: angle += 360 return angle
def cart2pol_2d_degree(pts, debug=True): ''' input a 2d point and convert to polar coordinate return for degree: [0, 360) ''' if debug: assert istuple(pts) or islist(pts) or isnparray( pts), 'input point is not correct' assert np.array(pts).size == 2, 'input point is not 2d points' x = pts[0] y = pts[1] rho = np.sqrt(x**2 + y**2) phi = math.degrees(np.arctan2(y, x)) while phi < 0: phi += 360 while phi >= 360.: phi -= 360 return (rho, phi)
def bbox_rotation_inv(bbox_in, angle_in_degree, image_shape, debug=True): ''' bbox_in is two coordinate angle is clockwise ''' if debug: assert isnparray(bbox_in) and bbox_in.size == 4, 'box is not correct' im_width = image_shape[1] im_height = image_shape[0] coor_in_tl = np.array([(bbox_in[0] - im_width/2)/im_width*2, (bbox_in[1] - im_height/2)/im_height*2, 1]) # normalization coor_in_br = np.array([(bbox_in[2] - im_width/2)/im_width*2, (bbox_in[3] - im_height/2)/im_height*2, 1]) # normalization # print(coor_in_tl) # print(coor_in_br) affine = np.array([[math.cos(rad(angle_in_degree)), math.sin(rad(angle_in_degree)), 0], [-math.sin(rad(angle_in_degree)), math.cos(rad(angle_in_degree)), 0]]) coor_out_tl = np.dot(coor_in_tl, affine.transpose()) coor_out_br = np.dot(coor_in_br, affine.transpose()) # print(coor_out_tl) # print(coor_out_br) bbox_recover = [coor_out_tl[0] * im_width/2 + im_width/2, coor_out_tl[1] * im_height/2 + im_height/2, coor_out_br[0] * im_width/2 + im_width/2, coor_out_br[1] * im_height/2 + im_height/2] bbox_recover = np.array(bbox_recover, dtype = float) return bbox_recover
def safe_ptsarray_occlusion(input_pts, warning=True, debug=True): ''' make sure to copy the pts array without modifying it and make the dimension to 3 x N the occlusion (3rd) row should contain 0, 1 or -1 parameters: input_pts: a list of 3 elements, a listoflist of 3 elements: e.g., [[1,2], [5,6], [0, 1]], a numpy array with shape or (3, N) or (3, ) outputs: np_pts: 3 X N numpy array, with the third row as the occlusion ''' if islist(input_pts): if islistoflist(input_pts): if debug: assert all(len(list_tmp) == 3 for list_tmp in input_pts), 'all sub-lists should have length of 3' np_pts = np.array(input_pts).transpose() else: if debug: assert len( input_pts ) == 3, 'the input pts list does not have a good shape' np_pts = np.array(input_pts).reshape((3, 1)) elif isnparray(input_pts): input_pts = input_pts.copy() if input_pts.shape == (3, ): np_pts = input_pts.reshape((3, 1)) else: np_pts = input_pts else: assert False, 'only list and numpy array for pts are supported' if debug: assert is2dptsarray_occlusion( np_pts), 'the input pts array does not have a good shape' return np_pts
def visualize_distribution(data, bin_size=None, vis=False, save_path=None, debug=True, closefig=True): ''' visualize the histogram of a data, which can be a dictionary or list or numpy array or tuple or a list of list ''' if debug: assert istuple(data) or isdict(data) or islist(data) or isnparray( data), 'input data is not correct' # convert data type if istuple(data): data = list(data) elif isdict(data): data = data.values() elif isnparray(data): data = data.tolist() num_bins = 1000.0 fig, ax = get_fig_ax_helper(fig=None, ax=None) # calculate bin size if bin_size is None: if islistoflist(data): max_value = np.max(np.max(data)) min_value = np.min(np.min(data)) else: max_value = np.max(data) min_value = np.min(data) bin_size = (max_value - min_value) / num_bins else: try: bin_size = float(bin_size) except TypeError: print('size of bin should be an float value') # plot if islistoflist(data): max_value = np.max(np.max(data)) min_value = np.min(np.min(data)) bins = np.arange(min_value - bin_size, max_value + bin_size, bin_size) # fixed bin size plt.xlim([min_value - bin_size, max_value + bin_size]) for data_list_tmp in data: if debug: assert islist(data_list_tmp), 'the nested list is not correct!' # plt.hist(data_list_tmp, bins=bins, alpha=0.3) sns.distplot(data_list_tmp, bins=bins, kde=False) # sns.distplot(data_list_tmp, bins=bins, kde=False) else: bins = np.arange( min(data) - 10 * bin_size, max(data) + 10 * bin_size, bin_size) # fixed bin size plt.xlim([min(data) - bin_size, max(data) + bin_size]) plt.hist(data, bins=bins, alpha=0.5) plt.title('distribution of data') plt.xlabel('data (bin size = %f)' % bin_size) plt.ylabel('count') return save_vis_close_helper(fig=fig, ax=ax, vis=vis, save_path=save_path, debug=debug, closefig=closefig)
def visualize_nearest_neighbor(featuremap_dict, num_neighbor=5, top_number=5, vis=True, save_csv=False, csv_save_path=None, save_vis=False, save_img=False, save_thumb_name='nearest_neighbor.png', img_src_folder=None, ext_filter='.jpg', nn_save_folder=None, debug=True): ''' visualize nearest neighbor for featuremap from images parameter: featuremap_dict: a dictionary contains image path as key, and featuremap as value, the featuremap needs to be numpy array with any shape. No flatten needed num_neighbor: number of neighbor to visualize, the first nearest is itself top_number: number of top to visualize, since there might be tons of featuremap (length of dictionary), we choose the top ten with lowest distance with their nearest neighbor csv_save_path: path to save .csv file which contains indices and distance array for all elements nn_save_folder: save the nearest neighbor images for top featuremap return: all_sorted_nearest_id: a 2d matrix, each row is a feature followed by its nearest neighbor in whole feature dataset, the column is sorted by the distance of all nearest neighbor each row selected_nearest_id: only top number of sorted nearest id ''' print('processing feature map to nearest neightbor.......') if debug: assert isdict(featuremap_dict), 'featuremap should be dictionary' assert all( isnparray(featuremap_tmp) for featuremap_tmp in featuremap_dict. values()), 'value of dictionary should be numpy array' assert isinteger( num_neighbor ) and num_neighbor > 1, 'number of neighborhodd is an integer larger than 1' if save_csv and csv_save_path is not None: assert is_path_exists_or_creatable( csv_save_path), 'path to save .csv file is not correct' if save_vis or save_img: if nn_save_folder is not None: # save image directly assert isstring(ext_filter), 'extension filter is not correct' assert is_path_exists( img_src_folder), 'source folder for image is not correct' assert all( isstring(path_tmp) for path_tmp in featuremap_dict.keys() ) # key should be the path for the image assert is_path_exists_or_creatable( nn_save_folder ), 'folder to save top visualized images is not correct' assert isstring( save_thumb_name), 'name of thumbnail is not correct' if ext_filter.find('.') == -1: ext_filter = '.%s' % ext_filter # flatten the feature map nn_feature_dict = dict() for key, featuremap_tmp in featuremap_dict.items(): nn_feature_dict[key] = featuremap_tmp.flatten() num_features = len(nn_feature_dict) # nearest neighbor featuremap = np.array(nn_feature_dict.values()) nearbrs = NearestNeighbors(n_neighbors=num_neighbor, algorithm='ball_tree').fit(featuremap) distances, indices = nearbrs.kneighbors(featuremap) if debug: assert featuremap.shape[ 0] == num_features, 'shape of feature map is not correct' assert indices.shape == ( num_features, num_neighbor), 'shape of indices is not correct' assert distances.shape == ( num_features, num_neighbor), 'shape of indices is not correct' # convert the nearest indices for all featuremap to the key accordingly id_list = nn_feature_dict.keys() max_length = len(max( id_list, key=len)) # find the maximum length of string in the key nearest_id = np.chararray(indices.shape, itemsize=max_length + 1) for x in range(nearest_id.shape[0]): for y in range(nearest_id.shape[1]): nearest_id[x, y] = id_list[indices[x, y]] if debug: assert list(nearest_id[:, 0]) == id_list, 'nearest neighbor has problem' # sort the feature based on distance print('sorting the feature based on distance') featuremap_distance = np.sum(distances, axis=1) if debug: assert featuremap_distance.shape == ( num_features, ), 'distance is not correct' sorted_indices = np.argsort(featuremap_distance) all_sorted_nearest_id = nearest_id[sorted_indices, :] # save to the csv file if save_csv and csv_save_path is not None: print('Saving nearest neighbor result as .csv to path: %s' % csv_save_path) with open(csv_save_path, 'w+') as file: np.savetxt(file, distances, delimiter=',', fmt='%f') np.savetxt(file, all_sorted_nearest_id, delimiter=',', fmt='%s') file.close() # choose the best to visualize selected_sorted_indices = sorted_indices[0:top_number] if debug: for i in range(num_features - 1): assert featuremap_distance[ sorted_indices[i]] < featuremap_distance[sorted_indices[ i + 1]], 'feature map is not well sorted based on distance' selected_nearest_id = nearest_id[selected_sorted_indices, :] if save_vis: fig, axarray = plt.subplots(top_number, num_neighbor) for index in range(top_number): for nearest_index in range(num_neighbor): img_path = os.path.join( img_src_folder, '%s%s' % (selected_nearest_id[index, nearest_index], ext_filter)) if debug: print('loading image from %s' % img_path) img = imread(img_path) if isgrayimage_dimension(img): axarray[index, nearest_index].imshow(img, cmap='gray') elif iscolorimage_dimension(img): axarray[index, nearest_index].imshow(img) else: assert False, 'unknown error' axarray[index, nearest_index].axis('off') save_thumb = os.path.join(nn_save_folder, save_thumb_name) fig.savefig(save_thumb) if vis: plt.show() plt.close(fig) # save top visualization to the folder if save_img and nn_save_folder is not None: for top_index in range(top_number): file_list = selected_nearest_id[top_index] save_subfolder = os.path.join(nn_save_folder, file_list[0]) mkdir_if_missing(save_subfolder) for file_tmp in file_list: file_src = os.path.join(img_src_folder, '%s%s' % (file_tmp, ext_filter)) save_path = os.path.join(save_subfolder, '%s%s' % (file_tmp, ext_filter)) if debug: print('saving %s to %s' % (file_src, save_path)) shutil.copyfile(file_src, save_path) return all_sorted_nearest_id, selected_nearest_id
def visualize_ced(normed_mean_error_dict, error_threshold, normalized=True, truncated_list=None, display2terminal=True, display_list=None, title='2D PCK curve', debug=True, vis=False, pck_savepath=None, table_savepath=None, closefig=True): ''' visualize the cumulative error distribution curve (alse called NME curve or pck curve) all parameters are represented by percentage parameter: normed_mean_error_dict: a dictionary whose keys are the method name and values are (N, ) numpy array to represent error in evaluation error_threshold: threshold to display in x axis return: AUC: area under the curve MSE: mean square error ''' if debug: assert isdict( normed_mean_error_dict ), 'the input normalized mean error dictionary is not correct' assert islogical( normalized), 'the normalization flag should be logical' if normalized: assert error_threshold > 0 and error_threshold < 100, 'threshold percentage is not well set' if save: assert is_path_exists_or_creatable( pck_savepath ), 'please provide a valid path to save the pck results' assert is_path_exists_or_creatable( table_savepath ), 'please provide a valid path to save the table results' assert isstring(title), 'title is not correct' if truncated_list is not None: assert islistofscalar( truncated_list), 'the input truncated list is not correct' if display_list is not None: assert islist(display_list) and len( display_list) == len(normed_mean_error_dict ), 'the input display list is not correct' assert CHECK_EQ_LIST_UNORDERED( display_list, normed_mean_error_dict.keys(), debug=debug ), 'the input display list does not match the error dictionary key list' else: display_list = normed_mean_error_dict.keys() # set display parameters width, height = 1000, 800 legend_fontsize = 10 scale_distance = 48.8 line_index, color_index = 0, 0 figsize = width / float(dpi), height / float(dpi) fig = plt.figure(figsize=figsize) # set figure handle num_bins = 1000 if normalized: maximum_x = 1 scale = num_bins / 100 else: maximum_x = error_threshold + 1 scale = num_bins / maximum_x x_axis = np.linspace( 0, maximum_x, num_bins) # error axis, percentage of normalization factor y_axis = np.zeros(num_bins) interval_y = 10 interval_x = 1 plt.xlim(0, error_threshold) plt.ylim(0, 100) plt.yticks(np.arange(0, 100 + interval_y, interval_y)) plt.xticks(np.arange(0, error_threshold + interval_x, interval_x)) plt.grid() plt.title(title, fontsize=20) if normalized: plt.xlabel('Normalized error euclidean distance (%)', fontsize=16) else: plt.xlabel('Absolute error euclidean distance', fontsize=16) # calculate metrics for each method num_methods = len(normed_mean_error_dict) num_images = len(normed_mean_error_dict.values()[0]) metrics_dict = dict() metrics_table = list() table_title = ['Method Name / Metrics', 'AUC', 'MSE'] append2title = False assert num_images > 0, 'number of error array should be larger than 0' for ordered_index in range(num_methods): method_name = display_list[ordered_index] normed_mean_error = normed_mean_error_dict[method_name] if debug: assert isnparray( normed_mean_error ) and normed_mean_error.ndim == 1, 'shape of error distance is not good' assert len( normed_mean_error ) == num_images, 'number of testing images should be equal for all methods' assert len(linestyle_set) * len(color_set) >= len( normed_mean_error_dict) color_tmp = color_set[color_index] line_tmp = linestyle_set[line_index] for i in range(num_bins): y_axis[i] = float( (normed_mean_error < x_axis[i]).sum()) / num_images # percentage of error # calculate area under the curve and mean square error entry = dict() entry['AUC'] = np.sum(y_axis[:error_threshold * scale]) / ( error_threshold * scale) # bigger, better entry['MSE'] = np.mean(normed_mean_error) # smaller, better metrics_table_tmp = [ str(method_name), '%.2f' % (entry['AUC']), '%.1f' % (entry['MSE']) ] if truncated_list is not None: tmse_dict = calculate_truncated_mse(normed_mean_error.tolist(), truncated_list, debug=debug) for threshold in truncated_list: entry['AUC/%s' % threshold] = np.sum(y_axis[:error_threshold * scale]) / ( error_threshold * scale) # bigger, better entry['MSE/%s' % threshold] = tmse_dict[threshold]['T-MSE'] entry['percentage/%s' % threshold] = tmse_dict[threshold]['percentage'] if not append2title: table_title.append('AUC/%s' % threshold) table_title.append('MSE/%s' % threshold) table_title.append('pct/%s' % threshold) metrics_table_tmp.append('%.2f' % (entry['AUC/%s' % threshold])) metrics_table_tmp.append('%.1f' % (entry['MSE/%s' % threshold])) metrics_table_tmp.append( '%.1f' % (100 * entry['percentage/%s' % threshold]) + '%') # print metrics_table_tmp metrics_table.append(metrics_table_tmp) append2title = True metrics_dict[method_name] = entry # draw label = '%s, AUC: %.2f, MSE: %.1f (%.0f um)' % ( method_name, entry['AUC'], entry['MSE'], entry['MSE'] * scale_distance) if normalized: plt.plot(x_axis * 100, y_axis * 100, color=color_tmp, linestyle=line_tmp, label=label, lw=3) else: plt.plot(x_axis, y_axis * 100, color=color_tmp, linestyle=line_tmp, label=label, lw=3) plt.legend(loc=4, fontsize=legend_fontsize) color_index += 1 if color_index / len(color_set) == 1: line_index += 1 color_index = color_index % len(color_set) # plt.grid() plt.ylabel('{} Test Images (%)'.format(num_images), fontsize=16) save_vis_close_helper(fig=fig, ax=None, vis=vis, transparent=False, save_path=pck_savepath, debug=debug, closefig=closefig) # reorder the table order_index_list = [ display_list.index(method_name_tmp) for method_name_tmp in normed_mean_error_dict.keys() ] order_index_list = [0] + [ order_index_tmp + 1 for order_index_tmp in order_index_list ] # print table to terminal metrics_table = [table_title] + metrics_table # metrics_table = list_reorder([table_title] + metrics_table, order_index_list, debug=debug) table = AsciiTable(metrics_table) if display2terminal: print('\nprint detailed metrics') print(table.table) # save table to file if table_savepath is not None: table_file = open(table_savepath, 'w') table_file.write(table.table) table_file.close() if display2terminal: print('\nsave detailed metrics to %s' % table_savepath) return metrics_dict, metrics_table
def generate_hdf5(data_src, save_dir, data_name='data', batch_size=1, ext_filter='png', label_src1=None, label_name1='label', label_preprocess_function1=identity, label_range1=None, label_src2=None, label_name2='label2', label_preprocess_function2=identity, label_range2=None, debug=True, vis=False): ''' # this function creates data in hdf5 format from a image path # input parameter # data_src: source of image data, which can be a list of image path, a txt file contains a list of image path, a folder contains a set of images, a list of numpy array image data # label_src: source of label data, which can be none, a file contains a set of labels, a dictionary of labels, a 1-d numpy array data, a list of label data # save_dir: where to store the hdf5 data # batch_size: how many image to store in a single hdf file # ext_filder: what format of data to use for generating hdf5 data ''' # parse input assert is_path_exists_or_creatable( save_dir), 'save path should be a folder to save all hdf5 files' mkdir_if_missing(save_dir) assert isstring( data_name), 'dataset name is not correct' # name for hdf5 data # convert data source to a list of numpy array image data if isfolder(data_src): print 'data is loading from %s with extension .%s' % (data_src, ext_filter) filelist, num_data = load_list_from_folder(data_src, ext_filter=ext_filter) datalist = None elif isfile(data_src): print 'data is loading from %s with extension .%s' % (data_src, ext_filter) filelist, num_data = load_list_from_file(data_src) datalist = None elif islist(data_src): if debug: assert all( isimage(data_tmp) for data_tmp in data_src ), 'input data source is not a list of numpy array image data' datalist = data_src num_data = len(datalist) filelist = None else: assert False, 'data source format is not correct.' if debug: assert (datalist is None and filelist is not None) or ( filelist is None and datalist is not None), 'data is not correct' if datalist is not None: assert len(datalist) == num_data, 'number of data is not equal' if filelist is not None: assert len(filelist) == num_data, 'number of data is not equal' # convert label source to a list of numpy array label if label_src1 is None: labeldict1 = None labellist1 = None elif isfile(label_src1): assert is_path_exists(label_src1), 'file not found' _, _, ext = fileparts(label_src1) assert ext == '.json', 'only json extension is supported' labeldict1 = json.load(label_src1) num_label1 = len(labeldict1) assert num_data == num_label1, 'number of data and label is not equal.' labellist1 = None elif isdict(label_src1): labeldict1 = label_src1 labellist1 = None elif isnparray(label_src1): if debug: assert label_src1.ndim == 1, 'only 1-d label is supported' labeldict1 = None labellist1 = label_src1 elif islist(label_src1): if debug: assert all( np.array(label_tmp).size == 1 for label_tmp in label_src1), 'only 1-d label is supported' labellist1 = label_src1 labeldict1 = None else: assert False, 'label source format is not correct.' assert isfunction(label_preprocess_function1 ), 'label preprocess function is not correct.' # convert label source to a list of numpy array label if label_src2 is None: labeldict2 = None labellist2 = None elif isfile(label_src2): assert is_path_exists(label_src2), 'file not found' _, _, ext = fileparts(label_src2) assert ext == '.json', 'only json extension is supported' labeldict2 = json.load(label_src2) num_label2 = len(labeldict2) assert num_data == num_label2, 'number of data and label is not equal.' labellist2 = None elif isdict(label_src2): labeldict2 = label_src2 labellist2 = None elif isnparray(label_src2): if debug: assert label_src2.ndim == 1, 'only 1-d label is supported' labeldict2 = None labellist2 = label_src2 elif islist(label_src2): if debug: assert all( np.array(label_tmp).size == 1 for label_tmp in label_src2), 'only 1-d label is supported' labellist2 = label_src2 labeldict2 = None else: assert False, 'label source format is not correct.' assert isfunction(label_preprocess_function2 ), 'label preprocess function is not correct.' # warm up if datalist is not None: size_data = datalist[0].shape else: size_data = imread(filelist[0]).shape if labeldict1 is not None: if debug: assert isstring(label_name1), 'label name is not correct' labels1 = np.zeros((batch_size, 1), dtype='float32') # label_value1 = [float(label_tmp_char) for label_tmp_char in labeldict1.values()] # label_range1 = np.array([min(label_value1), max(label_value1)]) if labellist1 is not None: labels1 = np.zeros((batch_size, 1), dtype='float32') # label_range1 = [np.min(labellist1), np.max(labellist1)] if label_src1 is not None and debug: assert label_range1 is not None, 'label range is not correct' assert (labeldict1 is not None and labellist1 is None) or ( labellist1 is not None and labeldict1 is None), 'label is not correct' if labeldict2 is not None: if debug: assert isstring(label_name2), 'label name is not correct' labels2 = np.zeros((batch_size, 1), dtype='float32') # label_value2 = [float(label_tmp_char) for label_tmp_char in labeldict2.values()] # label_range2 = np.array([min(label_value2), max(label_value2)]) if labellist2 is not None: labels2 = np.zeros((batch_size, 1), dtype='float32') # label_range2 = [np.min(labellist2), np.max(labellist2)] if label_src2 is not None and debug: assert label_range2 is not None, 'label range is not correct' assert (labeldict2 is not None and labellist2 is None) or ( labellist2 is not None and labeldict2 is None), 'label is not correct' # start generating count_hdf = 1 # count number of hdf5 file clock = Timer() datalist_batch = list() for i in xrange(num_data): clock.tic() if filelist is not None: imagefile = filelist[i] _, name, _ = fileparts(imagefile) img = imread(imagefile).astype('float32') max_value = np.max(img) if max_value > 1 and max_value <= 255: img = img / 255.0 # [rows,col,channel,numbers], scale the image data to (0, 1) if debug: min_value = np.min(img) assert min_value >= 0 and min_value <= 1, 'data is not in [0, 1]' if datalist is not None: img = datalist[i] if debug: assert size_data == img.shape datalist_batch.append(img) # process label if labeldict1 is not None: if debug: assert len(filelist) == len( labeldict1), 'file list is not equal to label dictionary' labels1[i % batch_size, 0] = float(labeldict1[name]) if labellist1 is not None: labels1[i % batch_size, 0] = float(labellist1[i]) if labeldict2 is not None: if debug: assert len(filelist) == len( labeldict2), 'file list is not equal to label dictionary' labels2[i % batch_size, 0] = float(labeldict2[name]) if labellist2 is not None: labels2[i % batch_size, 0] = float(labellist2[i]) # save to hdf5 if i % batch_size == 0: data = preprocess_image_caffe( datalist_batch, debug=debug, vis=vis ) # swap channel, transfer from list of HxWxC to NxCxHxW # write to hdf5 format if filelist is not None: save_path = os.path.join(save_dir, '%s.hdf5' % name) else: save_path = os.path.join(save_dir, 'image_%010d.hdf5' % count_hdf) h5f = h5py.File(save_path, 'w') h5f.create_dataset(data_name, data=data, dtype='float32') if (labeldict1 is not None) or (labellist1 is not None): # print(labels1) labels1 = label_preprocess_function1(data=labels1, data_range=label_range1, debug=debug) # print(labels1) h5f.create_dataset(label_name1, data=labels1, dtype='float32') labels1 = np.zeros((batch_size, 1), dtype='float32') if (labeldict2 is not None) or (labellist2 is not None): labels2 = label_preprocess_function2(data=labels2, data_range=label_range2, debug=debug) h5f.create_dataset(label_name2, data=labels2, dtype='float32') labels2 = np.zeros((batch_size, 1), dtype='float32') h5f.close() count_hdf = count_hdf + 1 del datalist_batch[:] if debug: assert len(datalist_batch) == 0, 'list has not been cleared' average_time = clock.toc() print( 'saving to %s: %d/%d, average time:%.3f, elapsed time:%s, estimated time remaining:%s' % (save_path, i + 1, num_data, average_time, convert_secs2time(average_time * i), convert_secs2time(average_time * (num_data - i)))) return count_hdf - 1, num_data
def visualize_bbox(input_bbox, linewidth=0.5, edge_color_index=15, scores=None, threshold=0.0, textsize=8, fig=None, ax=None, save_path=None, vis=False, warning=True, debug=True, closefig=True): ''' visualize a set of bounding box parameters: input_bbox: a list of 4 elements, a listoflist of 4 elements: e.g., [[1,2,3,4], [5,6,7,8]], a numpy array with shape or (N, 4) or (4, ) TLBR format scores: a list of floating numbers representing the confidences ''' if islist(input_bbox) and len(input_bbox) == 0: return save_vis_close_helper(fig=fig, ax=ax, vis=vis, save_path=save_path, warning=warning, debug=debug, closefig=closefig) elif isnparray(input_bbox) and input_bbox.size == 0: return save_vis_close_helper(fig=fig, ax=ax, vis=vis, save_path=save_path, warning=warning, debug=debug, closefig=closefig) np_bboxes = safe_bbox(input_bbox, warning=warning, debug=debug) if debug: assert bboxcheck_TLBR( np_bboxes, warning=warning, debug=debug), 'input bounding boxes are not correct' edge_color = color_set_big[edge_color_index % len(color_set_big)] np_bboxes = bbox_TLBR2TLWH( np_bboxes, warning=warning, debug=debug) # convert TLBR format to TLWH format for bbox_index in range(np_bboxes.shape[0]): bbox_tmp = np_bboxes[bbox_index, :] if scores is not None: score = float(scores[bbox_index]) if score < threshold: continue caption = '{:.2f}'.format(score) # score = str(scores[bbox_index]) # caption = '%s' % (score) ax.text(bbox_tmp[0], bbox_tmp[1] + textsize, caption, color='r', size=textsize, backgroundcolor='none') ax.add_patch( plt.Rectangle((bbox_tmp[0], bbox_tmp[1]), bbox_tmp[2], bbox_tmp[3], fill=False, edgecolor=edge_color, linewidth=linewidth)) return save_vis_close_helper(fig=fig, ax=ax, vis=vis, save_path=save_path, warning=warning, debug=debug, closefig=closefig)