def find_transform(source, target, source_points, target_points): y_size, x_size = np.shape(source) max_size = max(y_size, x_size) threshold = utils.round_up_to_odd(20 * max_size / 2048) transform_affine, ransac_affine_failed = ransac_partially_affine(source_points, target_points, 0.99, threshold) if ransac_affine_failed: threshold = utils.round_up_to_odd(30 * max_size / 2048) transform_affine, ransac_affine_failed = ransac_partially_affine(source_points, target_points, 0.90, threshold) threshold = utils.round_up_to_odd(20 * max_size / 2048) transform_rigid, ransac_rigid_failed = ransac_rigid(source_points, target_points, 0.99, threshold) if ransac_rigid_failed: threshold = utils.round_up_to_odd(30 * max_size / 2048) transform_rigid, ransac_rigid_failed = ransac_rigid(source_points, target_points, 0.90, threshold) return transform_affine, transform_rigid, ransac_affine_failed, ransac_rigid_failed
def cv_initial_alignment(source, target, echo=True): failed = False try: y_size, x_size = source.shape source = (source * 255).astype(np.uint8) target = (target * 255).astype(np.uint8) max_size = max(y_size, x_size) smoothing_size = utils.round_up_to_odd(max_size / 2048 * 31) source = cv2.GaussianBlur(source, (smoothing_size, smoothing_size), 0) target = cv2.GaussianBlur(target, (smoothing_size, smoothing_size), 0) if echo: print("SURFN: ") print() surfn_transform, surfn_score, surfn_failed = calculate_transform(source, target, echo, "surfn") if echo: print() print("SURFE: ") print() surfe_transform, surfe_score, surfe_failed = calculate_transform(source, target, echo, "surfe") if echo: print() print("ORB: ") print() orb_transform, orb_score, orb_failed = calculate_transform(source, target, echo, "orb") if echo: print() print("SIFT: ") print() sift_transform, sift_score, sift_failed = calculate_transform(source, target, echo, "sift") if echo: print("SURFN score:", surfn_score, "SURFN failed: ", surfn_failed) print("SURFE score:", surfe_score, "SURFE failed: ", surfe_failed) print("ORB score:", orb_score, "ORB failed: ", orb_failed) print("SIFT score:", sift_score, "SIFT failed: ", sift_failed) scores = np.array([surfn_score, surfe_score, orb_score, sift_score]) transforms = np.array([surfn_transform, surfe_transform, orb_transform, sift_transform]) best_id = np.argmax(scores) if scores[best_id] == 0: failed = True transform = np.eye(3) u_x, u_y = np.zeros(source.shape), np.zeros(source.shape) else: failed = False transform = transforms[best_id] u_x, u_y = utils.rigid_dot(source, np.linalg.inv(transform)) except: failed = True transform = np.eye(3) u_x, u_y = np.zeros(source.shape), np.zeros(source.shape) return u_x, u_y, transform, failed
def ORB_calculation(source, target): # Magic numbers below y_size, x_size = source.shape max_size = max(y_size, x_size) num_features = 5000 scale_factor = 1.3 num_levels = 8 edge_threshold = 60 first_level = 0 wta_k = 3 patch_size = utils.round_up_to_odd(35 * max_size / 2048) fast_threshold = utils.round_up_to_odd(25 * max_size / 2048) orb = cv2.ORB_create(num_features, scale_factor, num_levels, edge_threshold, first_level, wta_k, cv2.ORB_HARRIS_SCORE, patch_size, fast_threshold) source_keypoints, source_descriptors = orb.detectAndCompute(source, None) target_keypoints, target_descriptors = orb.detectAndCompute(target, None) return source_keypoints, source_descriptors, target_keypoints, target_descriptors
def threshold_adaptive(ndarray, method, blocksize=5, offset=0): # Cast to 16-bit #ndarray = convert_array_type(ndarray, 'int16') #Inizialize method_list = ['Mean', 'Gaussian', 'Sauvola', 'Niblack'] if method not in method_list: raise Exception('Mode has to be amond the following:\n' + str(method_list)) blocksize = round_up_to_odd(blocksize) #For 2D images array needs to be reshaped to run properly through next cycle if len(ndarray.shape) < 3: converted_image = [img_as_ubyte(ndarray)] else: converted_image = img_as_ubyte(ndarray) #Cycle through image outputImage = [] for i in range(len(converted_image)): if method == 'Mean': outputImage.append( cv2.adaptiveThreshold(converted_image[i], 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, blocksize, offset)) elif method == 'Gaussian': outputImage.append( cv2.adaptiveThreshold(converted_image[i], 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blocksize, offset)) elif method == 'Sauvola': outputImage.append(threshold_sauvola(converted_image[i])) elif method == 'Niblack': outputImage.append(threshold_niblack(converted_image[i])) else: raise LookupError('Not a valid method!') #Remove singleton dimension (eg. if image was 2D) outputImage = np.squeeze(np.array(outputImage) > 0).astype(np.uint8) return convert_array_type(outputImage, 'int8')
def calc_fixPos(etdata, fix, w=50): """ TODO: dublicate function. Update to use calc_event_data """ data=etdata.data ws=round_up_to_odd(w/1000.0*etdata.fs) fix_pos=[] for f in fix: ind_s=f[0]+ws ind_s = ind_s if ind_s < f[1] else f[1] ind_e=f[1]-ws ind_e = ind_e if ind_e > f[0] else f[0] posx_s = np.nanmean(data[f[0]:ind_s]['x']) posy_s = np.nanmean(data[f[0]:ind_s]['y']) posx_e = np.nanmean(data[ind_e:f[1]]['x']) posy_e = np.nanmean(data[ind_e:f[1]]['y']) fix_pos.append([posx_s, posx_e, posy_s, posy_e]) return np.array(fix_pos)
def calc_event_data(etdata, evt, w = {255:1, 0: 1, 1: 50, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 'vel': 18, 'etdq': 200}, ): """Calculates event parameters. Parameters: etdata -- an instance of ETData evt -- compact event vector w -- dictionary of context to take into account for each event type; in ms Returns: posx_s -- onset position, horizontal posx_e -- offset position, horizontal posy_s -- onset position, vertical posy_e -- offset position, vertical posx_mean -- mean postion, horizontal posy_mean -- mean postion, vertical posx_med -- median postion, horizontal posy_med -- median postion, vertical pv -- peak velocity pv_index -- index for peak velocity rms -- precision, 2D rms std -- precision, 2D std """ #init params data = etdata.data fs = etdata.fs e = {k:v for k, v in zip(['s', 'e', 'evt'], evt)} ws = w[e['evt']] ws = 1 if not(ws > 1) else round_up_to_odd(ws/1000.0*fs, min_val=3) ws_vel = round_up_to_odd(w['vel']/1000.0*fs, min_val=3) w_etdq = int(w['etdq']/1000.*fs) #calculate velocity using Savitzky-Golay filter vel = np.hypot(sg.savgol_filter(data['x'], ws_vel, 2, 1), sg.savgol_filter(data['y'], ws_vel, 2, 1))*fs ind_s = e['s']+ws ind_s = ind_s if ind_s < e['e'] else e['e'] ind_e = e['e']-ws ind_e = ind_e if ind_e > e['s'] else e['s'] posx_s = np.nanmean(data[e['s']:ind_s]['x']) posy_s = np.nanmean(data[e['s']:ind_s]['y']) posx_e = np.nanmean(data[ind_e:e['e']]['x']) posy_e = np.nanmean(data[ind_e:e['e']]['y']) posx_mean = np.nanmean(data[e['s']:e['e']]['x']) posy_mean = np.nanmean(data[e['s']:e['e']]['y']) posx_med = np.nanmedian(data[e['s']:e['e']]['x']) posy_med = np.nanmedian(data[e['s']:e['e']]['y']) pv = np.max(vel[e['s']:e['e']]) pv_index = e['s']+ np.argmax(vel[e['s']:e['e']]) if e['e']-e['s']>w_etdq: x_ = rolling_window(data[e['s']:e['e']]['x'], w_etdq) y_ = rolling_window(data[e['s']:e['e']]['y'], w_etdq) std = np.median(np.hypot(np.std(x_, axis=1), np.std(y_, axis=1))) rms = np.median(np.hypot(np.sqrt(np.mean(np.diff(x_)**2, axis=1)), np.sqrt(np.mean(np.diff(y_)**2, axis=1)))) else: std = 0 rms = 0 return posx_s, posx_e, posy_s, posy_e, posx_mean, posy_mean, posx_med, posy_med, pv, pv_index, rms, std
def extractFeatures(etdata, **kwargs): '''Extracts features for IRF ''' #get parameters data = etdata.data w, w_vel, w_dir = kwargs['w'], kwargs['w_vel'], kwargs['w_dir'] tic = time.time() #find sampling rate fs = etdata.fs #window size for spatial measures in samples ws = round_up_to_odd(w/1000.0*fs+1) #window size in samples for velocity calculation ws_vel = round_up_to_odd(w_vel/1000.0*fs) #window size in samples for direction calculation ws_dir = round_up_to_odd(w_dir/1000.0*fs) maskInterp = np.zeros(len(data), dtype=np.bool) '''Legacy code. Interpolates through missing points. if kwargs.has_key('interp') and kwargs['interp']: r = np.arange(len(data)) _mask = np.isnan(data['x']) | np.isnan(data['y']) fx = interp.PchipInterpolator(r[~_mask], data[~_mask]['x'], extrapolate=True) fy = interp.PchipInterpolator(r[~_mask], data[~_mask]['y'], extrapolate=True) data['x'][_mask]=fx(r[_mask]) data['y'][_mask]=fy(r[_mask]) maskInterp = _mask ''' #prepare data for vectorized processing ws_pad=(max((ws, ws_vel, ws_dir))-1)/2 x_padded = np.pad(data['x'], (ws_pad, ws_pad), 'constant', constant_values=np.nan) y_padded = np.pad(data['y'], (ws_pad, ws_pad), 'constant', constant_values=np.nan) ws_dir_pad=(ws_dir-1)/2 x_padded_dir=np.pad(data['x'], (ws_dir_pad, ws_dir_pad), 'constant', constant_values=np.nan) y_padded_dir=np.pad(data['y'], (ws_dir_pad, ws_dir_pad), 'constant', constant_values=np.nan) x_windowed = rolling_window(x_padded, ws) y_windowed = rolling_window(y_padded, ws) dx_windowed = rolling_window(np.diff(x_padded), ws-1) dy_windowed = rolling_window(np.diff(y_padded), ws-1) x_windowed_dir = rolling_window(np.diff(x_padded_dir), ws_dir-1) y_windowed_dir = rolling_window(np.diff(y_padded_dir), ws_dir-1) #%%Extract features features=dict() #sampling rate features['fs'] = np.ones(len(data))*fs for d, dd in zip(['x', 'y'], [x_windowed, y_windowed]): #difference between positions of preceding and succeding windows, #aka tobii feature, together with data quality features and its variants means=np.nanmean(dd, axis = 1) meds=np.nanmedian(dd, axis = 1) features['mean-diff-%s'%d] = np.roll(means, -(ws-1)/2) - \ np.roll(means, (ws-1)/2) features['med-diff-%s'%d] = np.roll(meds, -(ws-1)/2) - \ np.roll(meds, (ws-1)/2) #standard deviation features['std-%s'%d] = np.nanstd(dd, axis=1) features['std-next-%s'%d] = np.roll(features['std-%s'%d], -(ws-1)/2) features['std-prev-%s'%d] = np.roll(features['std-%s'%d], (ws-1)/2) features['mean-diff']= np.hypot(features['mean-diff-x'], features['mean-diff-y']) features['med-diff']= np.hypot(features['med-diff-x'], features['med-diff-y']) features['std'] = np.hypot(features['std-x'], features['std-y']) features['std-diff'] = np.hypot(features['std-next-x'], features['std-next-y']) - \ np.hypot(features['std-prev-x'], features['std-prev-y']) #BCEA P = 0.68 #cumulative probability of area under the multivariate normal k = np.log(1/(1-P)) #rho = [np.corrcoef(px, py)[0,1] for px, py in zip(x_windowed, y_windowed)] rho = vcorrcoef(x_windowed, y_windowed) features['bcea'] = 2 * k * np.pi * \ features['std-x'] * features['std-y'] * \ np.sqrt(1-np.power(rho,2)) features['bcea-diff'] = np.roll(features['bcea'], -(ws-1)/2) - \ np.roll(features['bcea'], (ws-1)/2) #RMS features['rms'] = np.hypot(np.sqrt(np.mean(np.square(dx_windowed), axis=1)), np.sqrt(np.mean(np.square(dy_windowed), axis=1))) features['rms-diff'] = np.roll(features['rms'], -(ws-1)/2) - \ np.roll(features['rms'], (ws-1)/2) #disp, aka idt feature x_range = np.nanmax(x_windowed, axis=1) - np.nanmin(x_windowed, axis=1) y_range = np.nanmax(y_windowed, axis=1) - np.nanmin(y_windowed, axis=1) features['disp'] = x_range + y_range #velocity and acceleration features['vel']=np.hypot(sg.savgol_filter(data['x'], ws_vel, 2, 1), sg.savgol_filter(data['y'], ws_vel, 2, 1))*fs features['acc']=np.hypot(sg.savgol_filter(data['x'], ws_vel, 2, 2), sg.savgol_filter(data['y'], ws_vel, 2, 2))*fs**2 #rayleightest angl = np.arctan2(y_windowed_dir, x_windowed_dir) features['rayleightest'] = ast.rayleightest(angl, axis=1) #i2mc if kwargs.has_key('i2mc') and kwargs['i2mc'] is not None: features['i2mc'] = kwargs['i2mc']['finalweights'].flatten() else: features['i2mc'] = np.zeros(len(data)) #remove padding and nans mask_nans = np.any([np.isnan(values) for key, values\ in features.iteritems()], axis=0) mask_pad = np.zeros_like(data['x'], dtype=np.bool) mask_pad[:ws_pad] = True mask_pad[-ws_pad:] = True mask = mask_nans | mask_pad | maskInterp features={key: values[~mask].astype(np.float32) for key, values \ in features.iteritems()} dtype = np.dtype(zip(features.keys(), itertools.repeat(np.float32))) features = np.core.records.fromarrays(features.values(), dtype=dtype) #return features toc = time.time() if kwargs.has_key('print_et') and kwargs['print_et']: print 'Feature extraction took %.3f s.'%(toc-tic) return features, ~mask
def ct_initial_alignment(source, target, echo=True): y_size, x_size = source.shape source = (source * 255).astype(np.uint8) target = (target * 255).astype(np.uint8) max_size = max(y_size, x_size) smoothing_size = utils.round_up_to_odd(max_size / 2048 * 31) source = cv2.GaussianBlur(source, (smoothing_size, smoothing_size), 0) target = cv2.GaussianBlur(target, (smoothing_size, smoothing_size), 0) ret_source, thresholded_source = fd.threshold_calculation_with_rotation(source) ret_target, thresholded_target = fd.threshold_calculation_with_rotation(target) xs_m = utils.round_up_to_odd(x_size * 20 / 2048) ys_m = utils.round_up_to_odd(y_size * 20 / 2048) struct = min([xs_m, ys_m]) thresholded_source = nd.binary_erosion(thresholded_source, structure=np.ones((struct, struct))).astype(np.uint8) thresholded_source = nd.binary_dilation(thresholded_source, structure=np.ones((struct, struct))).astype(np.uint8) thresholded_target = nd.binary_erosion(thresholded_target, structure=np.ones((struct, struct))).astype(np.uint8) thresholded_target = nd.binary_dilation(thresholded_target, structure=np.ones((struct, struct))).astype(np.uint8) Ms = cv2.moments(thresholded_source) Mt = cv2.moments(thresholded_target) cXs = Ms["m10"] / Ms["m00"] cYs = Ms["m01"] / Ms["m00"] cXt = Mt["m10"] / Mt["m00"] cYt = Mt["m01"] / Mt["m00"] transform_centroid = np.array([ [1, 0, (cXt-cXs)], [0, 1, (cYt-cYs)], [0, 0, 1]]) u_x_t, u_y_t = utils.rigid_dot(source, np.linalg.inv(transform_centroid)) failed = True angle_step = 2 initial_dice = utils.dice(thresholded_source, thresholded_target) if echo: print("Initial dice: ", initial_dice) best_dice = initial_dice for i in range(0, 360, angle_step): if echo: print("Current angle: ", i) rads = i * np.pi/180 matrix_1 = np.array([ [1, 0, cXt], [0, 1, cYt], [0, 0, 1], ]) matrix_i = np.array([ [np.cos(rads), -np.sin(rads), 0], [np.sin(rads), np.cos(rads), 0], [0, 0, 1], ]) matrix_2 = np.array([ [1, 0, -cXt], [0, 1, -cYt], [0, 0, 1], ]) matrix = matrix_1 @ matrix_i @ matrix_2 u_x, u_y = utils.rigid_dot(source, np.linalg.inv(matrix)) transformed_source = utils.warp_image(source, u_x + u_x_t, u_y + u_y_t) ret_transformed_source, thresholded_transformed_source = fd.threshold_calculation_with_threshold_with_rotation(transformed_source, ret_source) thresholded_transformed_source = nd.binary_erosion(thresholded_transformed_source, structure=np.ones((struct, struct))).astype(np.uint8) thresholded_transformed_source = nd.binary_dilation(thresholded_transformed_source, structure=np.ones((struct, struct))).astype(np.uint8) current_dice = utils.dice(thresholded_transformed_source, thresholded_target) if echo: print("Current dice: ", current_dice) if (current_dice > best_dice and current_dice > initial_dice + 0.10 and current_dice > 0.85) or (current_dice > 0.95 and current_dice > best_dice): failed = False best_dice = current_dice transform = matrix.copy() if echo: print("Current best dice: ", best_dice) if failed: transform = np.eye(3) final_transform = transform @ transform_centroid if echo: print("Calculated transform: ", final_transform) if failed: final_transform = np.eye(3) u_x, u_y = utils.rigid_dot(source, np.linalg.inv(final_transform)) return u_x, u_y, final_transform, failed