def check_corners(worm_matrix, length): height, width = worm_matrix.shape pixel_dense = {} worm_min = np.inf pop_list = [] for y in range(height): for x in range(width): if worm_matrix[y, x]: worm_near = ci.wormNearPixel(worm_matrix, y, x, search_range=1) if worm_near < worm_min: pixel_dense[(y, x)] = worm_near worm_min = worm_near for item in pixel_dense: if pixel_dense[item] > worm_min: pop_list.append(item) for item in pop_list: pixel_dense.pop(item) while item in pop_list: pop_list.remove(item) distance_dict = {} for pix1 in pixel_dense: for pix2 in pixel_dense: for pix3 in pixel_dense: if pix1 != pix2 and pix1 != pix3 and pix2 != pix3: dist_sum = ci.pointDistance(pix1, pix2) + ci.pointDistance( pix1, pix3) + ci.pointDistance(pix2, pix3) if dist_sum > length * CORNER_DISTANCE_CONSTANT: return False return True
def cleanSmallLoops(point_list): """ Removes small groupings of points from the point_list and returns the list Args: point_list: A list of points Returns: point_list: The list with any small loops at the end closed """ #Find the point that starts the circle for i in range(len(point_list)): point1 = point_list[i] if ci.checkClosePoint(point1,point_list): break point_dict = {} #Look for the most distant point for j in range(i,len(point_list)): point_dict[j] = ci.pointDistance(point1,point_list[j]) indx = max(point_dict, key=point_dict.get) #Remove the points following that distant point if indx - i < SMALLEST_LOOP: return point_list[0:indx] else: return point_list
def sortPoints(point_list): """ Put points in a recognizable order. The first point should be closest to (0,0) Args: point_list: The list of points to sort Returns: point_list: The sorted list of points """ first_distance = ci.pointDistance(point_list[0], (0, 0)) last_distance = ci.pointDistance(point_list[-1], (0, 0)) if first_distance < last_distance: return point_list else: point_list.reverse() return point_list
def calcTravel(x1s,y1s,x2s,y2s): """!!!!!Find something better for this!!!!!""" total_distance = 0 for i in range(len(x1s)-1): travel_dif = ci.pointDistance(((x1s[i]+x2s[i])/2,(y1s[i]+y2s[i])/2),((x1s[i+1]+x2s[i+1])/2,(y1s[i+1]+y2s[i+1])/2)) if travel_dif > 2: total_distance+=travel_dif return total_distance
def total_distance(self, other_cluster): """ Finds the total distance two clusters cover The first point of the next cluster will be linked to the last point of this cluster other_cluster: The cluster to attach at the end of this one """ return self.cml_distance + other_cluster.cml_distance + ci.pointDistance(self.point_list[-1], other_cluster.point_list[0])
def translateDistances(distance_list,prior_direction,next_point,worm_matrix,point_list): """ Determines which possible directions could be possible to move in based on inputs Args: distance_list: A list of values with the index representing a direction and the value representing how 'good' of a path this is prior_direction: The direction moved in previously next_point: The point to move from worm_matrix: The matrix describing what is 'worm' and what isn't point_list: The list of all previous points Returns: A direction in which to move in based on an evenly segmented circle """ possible_directions=[] if distance_list[0]==min(distance_list): possible_directions.extend([2,6]) if distance_list[1]==min(distance_list): possible_directions.extend([0,4]) if distance_list[3]==min(distance_list): possible_directions.extend([1,5]) if distance_list[2]==min(distance_list): possible_directions.extend([3,7]) limited_directions = [] for i in range(-2,3): v =prior_direction+i if v<0: v+=8 if v>7: v-=8 limited_directions.append(v) select_direct = intersection(possible_directions,limited_directions) backup = [] for item in select_direct: direction_list = [(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1)] test_change = direction_list[item] if not worm_matrix[next_point[0]+test_change[0],next_point[1]+test_change[1]]: select_direct.remove(item) backup.append(item) if len(select_direct) == 0: return backup[0] elif len(select_direct) > 1: direction_list = [(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1)] point_distance = {} for item in select_direct: test_change = direction_list[item] coord1 = (next_point[0]+test_change[0],next_point[1]+test_change[1]) test_change = direction_list sumv=0 for coord2 in point_list: sumv+=ci.pointDistance(coord1,coord2) point_distance[item] = sumv return max(point_distance,key=point_distance.get) return select_direct[0]
def trajSinIndex(point_list): step_length = [] turn_angle = [] for i in range(len(point_list)-1): step_length.append(ci.pointDistance(point_list[i],point_list[i+1])) if i != 0: turn_angle.append(sc.getAngle(point_list[i-1],point_list[i],point_list[i+1])) sigma = np.nanstd(np.array(turn_angle)) q = np.nanmean(np.array(step_length)) return 1.18 * sigma / np.sqrt(q)
def connect(self, other_point): """ Creates and returns a new cluster with the new point added to the end of this cluster """ total_size = self.size + other_point.size point_list = self.point_list + other_point.point_list total_distance = self.cml_distance + other_point.cml_distance + ci.pointDistance(self.point_list[-1], other_point.point_list[0]) return cluster(point_list,total_size,total_distance)
def getCmlDistance(worm_matrix, grayscale_matrix, worm_path=None): """ Segments the worm into point_num clusters, then finds the total change in slope. """ wormFront = ci.findFront(worm_matrix) skelList = lazySkeleton(worm_matrix) sum = 0 for i in range(0,len(skelList)-1,1): sum += ci.pointDistance(skelList[i],skelList[i+1]) return sum
def connection(self,other_point): """ Determines whether two clusters can form a valid connection --- other_point: The following cluster """ total_size = self.size + other_point.size total_distance = self.cml_distance + other_point.cml_distance total_distance += ci.pointDistance(self.point_list[-1], other_point.point_list[0]) if total_size <= MAX_SIZE and total_distance <= MAX_DISTANCE: return True else: return False
def midpoint(self): """ Returns an (x,y) that is equally distant from either end of the cluster, moving along points in the cluster """ mes_distance = 0 i = 1 point= self.point_list[0] prev_point = point while mes_distance < self.cml_distance/2: point = self.point_list[i] mes_distance += ci.pointDistance(prev_point,point) i+=1 return point
def allSingleAnalysis(worm_location, img_name, use_ci=True): """ Collects all data from a worm image Args: worm_location: The folder that the image is located in img_name: The image of the worm use_ci: Whether to use worm_location as a file path or as a raw image Returns: outnumpy: Numpy array of data """ try: # Get basic information about the image parsed_name = img_name.split("_") vid_id = parsed_name[1] frame = parsed_name[2] worm_id = parsed_name[3] x1 = parsed_name[5] y1 = parsed_name[6] x2 = parsed_name[7] y2 = parsed_name[8].split(".")[0] # Make the basic information: worm matrix, grayscale matrix, and skeleton if use_ci: worm_dict, grayscale_matrix = ci.getWormMatrices(worm_location + "/" + img_name) else: worm_dict, grayscale_matrix = ci.getFilelessWormMatrices( worm_location) worm_matrix = ci.findCenterWorm(worm_dict) skel_list = sc.lazySkeleton(worm_matrix) area = np.sum(worm_matrix) # Create a matrix with only the color values of worm pixels mult_matrix = np.multiply(worm_matrix, grayscale_matrix) # Set all pixels that aren't worm to nan mult_matrix[mult_matrix == 0] = np.nan # Take the average ignoring nan shade = np.nanmean(mult_matrix) # Create Simplified worm skeleton skel_simple = sc.makeFractionedClusters(skel_list, 7) # Take cumulative angle of simplified skeleton cml_angle = sc.getCmlAnglePoints(skel_simple) length = 0 for i in range(0, len(skel_list) - 1, 1): length += ci.pointDistance(skel_list[i], skel_list[i + 1]) # Find Max Width width_point = {} for i in range(0, len(skel_list)): point = skel_list[i] x = point[0] y = point[1] width_point[point] = estimateMaxWidth(point, worm_matrix) max_width = width_point[max(width_point, key=width_point.get)] mid_width = width_point[skel_list[round(len(skel_list) / 2)]] # Get Diagonals diagonals = sc.getDiagonalNum(worm_matrix, grayscale_matrix) # Get simplified skeleton coordinates skel_simple = sc.makeFractionedClusters(skel_list, 5) sorted_list = sortPoints(skel_simple) point1_x = sorted_list[0][0] point1_y = sorted_list[0][1] point2_x = sorted_list[1][0] point2_y = sorted_list[1][1] point3_x = sorted_list[2][0] point3_y = sorted_list[2][1] point4_x = sorted_list[3][0] point4_y = sorted_list[3][1] point5_x = sorted_list[4][0] point5_y = sorted_list[4][1] outnumpy = np.array([ frame, x1, y1, x2, y2, worm_id, area, shade, cml_angle, length, max_width, mid_width, diagonals, point1_x, point1_y, point2_x, point2_y, point3_x, point3_y, point4_x, point4_y, point5_x, point5_y ]) wc.check_worm(worm_dict, worm_matrix, outnumpy, skel_list) except Exception as E: # Show that something went wrong in console if SHOW_INVALID: print(img_name, "is invalid") print(E) # Show that the data is invalid outnumpy = np.array([ frame, x1, y1, x2, y2, worm_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) return outnumpy
def getValuesFrom(day,header,cur_data): for i in range(len(header)): if header[i] == "Worm ID": id_head = i elif header[i] == "Area": area_head = i elif header[i] == "Shade": shade_head = i elif header[i] == "Cumulative Angle": cml_angle_head = i elif header[i] == "Length": l_head = i elif header[i] == "Max Width": mxw_head = i elif header[i] == "Mid Width": mdw_head = i elif header[i] == "Diagonals": dgl_head = i elif header[i] == "x1": x1_head = i elif header[i] == "x2": x2_head = i elif header[i] == "y1": y1_head = i elif header[i] == "y2": y2_head = i elif header[i] == "# Video Frame" or header[i] == "Video Frame": frame_head = i avg_area = sum(cur_data[area_head])/len(cur_data[area_head]) avg_shade = sum(cur_data[shade_head])/len(cur_data[shade_head]) angle_variance = np.var(cur_data[cml_angle_head]) avg_length = sum(cur_data[l_head])/len(cur_data[l_head]) avg_mxw = sum(cur_data[mxw_head])/len(cur_data[mxw_head]) avg_mdw = sum(cur_data[mdw_head])/len(cur_data[mdw_head]) diagonal_variance = np.var(cur_data[dgl_head]) # Approximate total traveled distance # TODO: Threshold of change (<2.5?) total_travel = calcTravel(cur_data[x1_head],cur_data[y1_head],cur_data[x2_head],cur_data[y2_head]) travel_speed = total_travel / (cur_data[0][-1] - cur_data[0][0]) # TODO: Traja... stuff? x_arr = [] y_arr = [] time_arr = [] point_arr = [] for i in range(len(cur_data[x1_head])): x_arr.append((cur_data[x1_head][i] + cur_data[x2_head][i])/2) y_arr.append((cur_data[y1_head][i] + cur_data[y2_head][i])/2) time_arr.append(cur_data[frame_head][i]) point_arr.append([x_arr[-1],y_arr[-1]]) # TODO: Check if able to set noise of movement (extended or smaller groupings of motion) df = trj.TrajaDataFrame({'x':x_arr,'y':y_arr,'time':time_arr}) df_derivs = df.traja.get_derivatives() if SHOW_MOTION: plt.plot(df['x'],df['y']) avg_accel = np.nanmean(df_derivs["acceleration"]) max_accel = np.nanmax(df_derivs["acceleration"]) max_speed = np.nanmax(df_derivs["speed"]) avg_speed = np.nanmean(df_derivs["speed"]) point0 = ((cur_data[x1_head][0] + cur_data[x2_head][0])/2, (cur_data[y1_head][0] + cur_data[y2_head][0])/2) last_point = ((cur_data[x1_head][-1] + cur_data[x2_head][-1])/2, (cur_data[y1_head][-1] + cur_data[y2_head][-1])/2) rvr_sinuosity_index = total_travel / ci.pointDistance(point0,last_point) #try: clustP = sc.makeFractionedClusters(point_arr,8) if SHOW_MOTION: x, y = zip(*clustP) plt.scatter(x,y) plt.show() sinuosity_index = trajSinIndex(clustP) #except: # return np.array([float(day),cur_data[id_head][0],avg_area,avg_shade,angle_variance,avg_length,avg_mxw,avg_mdw,diagonal_variance,total_travel,travel_speed,avg_speed,max_speed,max_accel,avg_accel,rvr_sinuosity_index,sinuosity_index]) return np.array([float(day),cur_data[id_head][0],avg_area,avg_shade,angle_variance,avg_length,avg_mxw,avg_mdw,diagonal_variance,total_travel,travel_speed,avg_speed,max_speed,max_accel,avg_accel,rvr_sinuosity_index,sinuosity_index])