def filter(self, plants): for k, plant in enumerate(plants): if k == 0 or k == len(plants) - 1: continue last_plant = plants[k-1] next_plant = plants[k+1] last_dist = position_difference(last_plant.position, plant.position) next_dist = position_difference(next_plant.position, plant.position) min_dist = min(last_dist, next_dist) max_dist = max(last_dist, next_dist) if min_dist == 0: continue # protect against division by zero ratio = max_dist / min_dist if ratio <= self.spacing_thresh: continue # good spacing so don't need to do anything # Bad spacing so move plant to be in center avg_position = np.mean([last_plant.position, next_plant.position], axis=0) plants[k] = CreatedPlant(name='plant', position=avg_position, zone=last_plant.zone) self.num_plants_moved += 1
def find_actual_plant(self, possible_plants, segment): for possible_plant in possible_plants: distance_to_code = position_difference(possible_plant['position'], segment.start_code.position) closeness_penalty = self.calculate_closeness_penalty(distance_to_code) num_items_penalty = self.calculate_num_items_penalty(possible_plant) possible_plant['penalty'] = closeness_penalty + 0.2*num_items_penalty valid_plants = [p for p in possible_plants if not math.isnan(p['penalty'])] if len(valid_plants) == 0: best_plant = None # none of the possible plants worked out else: best_plant = sorted(valid_plants, key=lambda p: p['penalty'])[0] if best_plant is None: position = segment.start_code.position bounding_rect = None else: position = best_plant['position'] bounding_rect = best_plant['rect'] self.num_successfully_found_plants += 1 plant = Plant(name=segment.start_code.name, position=position, zone=segment.start_code.zone, bounding_rect=bounding_rect) return plant
def remove_plant_parts_close_to_code(plant_parts, code, closest_dist): '''Return updated plant part list such that none are within 'closest_dist' of code.''' filtered_plant_parts = [] for part in plant_parts: x, y = corner_rect_center(part['rect']) dist_to_item = position_difference((x, y), code.position) if dist_to_item > closest_dist: filtered_plant_parts.append(part) return filtered_plant_parts
def check_code_precision(merged_codes): # Sanity check that multiple references of the same code are all close to each other. largest_separation = 0 sum_separation = 0 sum_separation_count = 0 for code in merged_codes: for code_ref in code.all_refs: diff = position_difference(code.position, code_ref.position) sum_separation += diff sum_separation_count += 1 if diff > largest_separation: largest_separation = diff average_separation = 0 if sum_separation_count > 0: average_separation = sum_separation / sum_separation_count print "From average position largest separation is {} and average is {}".format(largest_separation, average_separation)
def warn_about_missing_single_code_lengths(single_segments, spacing_between_plants): num_good_lengths = 0 # how many groups have a close to expected length num_too_long = 0 # how many groups have a longer than expected length for k, segment in enumerate(single_segments[:-1]): next_segment = single_segments[k+1] expected_length = spacing_between_plants if len(segment.items) > 0 and len(next_segment.items) > 0: # Plants have been found. plant1 = segment.items[0] plant2 = next_segment.items[0] actual_length = position_difference(plant1.position, plant2.position) else: # Plants not found yet so just compare codes. actual_length = segment.length length_difference = actual_length - expected_length # how long segment can be without being flagged max_length = spacing_between_plants * 1.75 if segment.end_code.type == 'RowCode': continue # length won't be valid if actual_length > max_length: print "\nSingle segment {} to {} is {} feet too long.".format(segment.start_code.name, segment.end_code.name, length_difference * 100 / 30) print "{} to {}.".format(segment.start_code.parent_image_filename, segment.end_code.parent_image_filename) num_too_long += 1 if actual_length < 0.06: print "WARNING - segment {} to {} is way too short".format(segment.start_code.name, segment.end_code.name) print "\n------Single Segment Length Report------" print "Found {} single codes with close expected lengths".format(num_good_lengths) print "and {} that were too long".format(num_too_long) print "\n"
def write_out_missed_codes(self, found_codes, missed_code_filename, out_directory): missing_codes = [] for possibly_missed_code in self.possibly_missed_codes: was_actually_found = False for found_code in found_codes: pos_diff = position_difference(possibly_missed_code.position, found_code.position) if pos_diff < 0.12: was_actually_found = True break if not was_actually_found: missing_codes.append(possibly_missed_code) missed_code_filepath = os.path.join(out_directory, missed_code_filename) missed_code_file = open(missed_code_filepath, 'wb') missed_code_csv_writer = csv.writer(missed_code_file) # Create subdirectory to store extracted images in. extracted_img_out_directory = os.path.join(out_directory, 'extracted_images/') if not os.path.exists(extracted_img_out_directory): os.makedirs(extracted_img_out_directory) for k, missed_code in enumerate(missing_codes): parent_img = cv2.imread(missed_code.parent_filepath, cv2.CV_LOAD_IMAGE_COLOR) if parent_img is None: print 'Cannot open image: {}'.format(missed_code.parent_filepath) continue extracted_img = extract_square_image(parent_img, missed_code.rect, 60, rotated=True) extracted_img_filename = '{}_{}'.format(k, postfix_filename(missed_code.parent_filename, '_missed')) extracted_img_filepath = os.path.join(extracted_img_out_directory, extracted_img_filename) cv2.imwrite(extracted_img_filepath, extracted_img) x, y = rectangle_center(missed_code.rect) missed_code_csv_writer.writerow(['add_imaged_code', k, missed_code.parent_filename, int(x), int(y)])
items = sorted(items, key=lambda item: item.number_within_field) plant_spacings = [] if plant_spacing > 0: # Run spacing verification on single plants to double check no codes were missed. all_segments = all_segments_from_rows(rows) single_segments = [segment for segment in all_segments if segment.start_code.type == 'SingleCode'] warn_about_missing_single_code_lengths(single_segments, plant_spacing) # Run spacing verification on regular plants. last_plant = None for item in items: if item.type not in ['Plant', 'CreatedPlant']: continue if last_plant and last_plant.row == item.row: spacing = position_difference(last_plant.position, item.position) if spacing > plant_spacing * 1.5: print "{} between {} and {}".format(spacing, last_plant.number_within_field, item.number_within_field) plant_spacings.append(spacing) last_plant = item # generate a sub-directory in specified output directory to house all output files. out_directory = os.path.join(out_directory, time.strftime('results-%Y%m%d-%H%M%S/')) if not os.path.exists(out_directory): os.makedirs(out_directory) if survey_filepath != 'none': if not os.path.exists(survey_filepath): print "Survey file doesn't exist {}".format(survey_filepath) sys.exit(ExitReason.bad_arguments)
if len(geo_images) == 0 or len(all_codes) == 0: print "Couldn't load any geo images or codes from input directory {}".format(input_directory) sys.exit(ExitReason.no_geo_images) # Merge items so they're unique. One code references other instances of that same code. merged_codes = merge_items(all_codes, max_distance=500) print '{} unique codes.'.format(len(merged_codes)) # Sanity check that multiple references of the same code are all close to each other. largest_separation = 0 sum_separation = 0 sum_separation_count = 0 for code in merged_codes: for code_ref in code.all_refs: diff = position_difference(code.position, code_ref.position) sum_separation += diff sum_separation_count += 1 if diff > largest_separation: largest_separation = diff average_separation = 0 if sum_separation_count > 0: average_separation = sum_separation / sum_separation_count print "From average position largest separation is {} and average is {} meters".format(largest_separation, average_separation) if not os.path.exists(out_directory): os.makedirs(out_directory) # Write everything out to CSV file.