void ns_spine_drawer::draw_mesh(const vector<ns_triangle_d> & mesh, const ns_color_8 & color, const unsigned int resize_factor,ns_image_standard & output){ //draw triangles for (unsigned int i = 0; i < mesh.size(); i++){ ns_triangle_i val; for (unsigned int t = 0; t < 3; t++){ val.vertex[t].x = (int)(resize_factor*mesh[i].vertex[t].x); val.vertex[t].y = (int)(resize_factor*mesh[i].vertex[t].y); } output.draw_line_color(val.vertex[0],val.vertex[1],color); output.draw_line_color(val.vertex[1],val.vertex[2],color); output.draw_line_color(val.vertex[2],val.vertex[0],color); } }
void ns_process_dynamic_stretch(ns_image_standard & im){ unsigned char new_top(250), new_bottom(155); unsigned char up(255-new_top); unsigned char diff(new_top - new_bottom); for (unsigned int y = 0; y < im.properties().height; y++){ for (unsigned int x = 0; x < im.properties().width; x++){ unsigned char c(255-im[y][x]); if (c>new_top)c=new_top; else if (c<new_bottom)c=new_bottom; im[y][x] = (ns_8_bit)((255*(long)(c-new_bottom))/diff); } } }
void ns_detected_object_manager::remove_objects_found_in_static_mask(const ns_image_standard & static_mask){ vector<ns_detected_object *> objects_to_sort; objects_to_sort.assign(objects.begin(),objects.end()); objects.resize(0); vector<ns_detected_object *> not_in_static_mask; not_in_static_mask.reserve(objects.size()); try{ for (unsigned int i = 0; i < objects_to_sort.size(); i++){ unsigned int mask_overlay(0), total_area(0); if (objects_to_sort[i]->size.x > objects_to_sort[i]->bitmap().properties().width || objects_to_sort[i]->size.y > objects_to_sort[i]->bitmap().properties().height) throw ns_ex("Specified object: (position:") << objects_to_sort[i]->offset_in_source_image.x << "," << objects_to_sort[i]->offset_in_source_image.y << "; size: " << objects_to_sort[i]->size.x << "," << objects_to_sort[i]->size.y << ") is larger than it's bitmap " << objects_to_sort[i]->bitmap().properties().width << "," << objects_to_sort[i]->bitmap().properties().height; if (objects_to_sort[i]->offset_in_source_image.x + objects_to_sort[i]->size.x > static_mask.properties().width || objects_to_sort[i]->offset_in_source_image.y + objects_to_sort[i]->size.y > static_mask.properties().height) throw ns_ex("Specified object: (position:") << objects_to_sort[i]->offset_in_source_image.x << "," << objects_to_sort[i]->offset_in_source_image.y << "; size: " << objects_to_sort[i]->size.x << "," << objects_to_sort[i]->size.y << ") lies outside the static mask " << objects_to_sort[i]->bitmap().properties().width << "," << objects_to_sort[i]->bitmap().properties().height; for (unsigned int y = 0; y < objects_to_sort[i]->size.y; y++){ for (unsigned int x = 0; x < objects_to_sort[i]->size.x; x++){ total_area+=objects_to_sort[i]->bitmap()[y][x]; mask_overlay+= static_mask[y+objects_to_sort[i]->offset_in_source_image.y][x+objects_to_sort[i]->offset_in_source_image.x] && objects_to_sort[i]->bitmap()[y][x]; } } if (10*mask_overlay > 5*total_area){ //object is in the static mask. ns_safe_delete(objects_to_sort[i]); } else{ not_in_static_mask.push_back(objects_to_sort[i]); objects_to_sort[i] = 0; } } objects_to_sort.clear(); //put all the objects we want to keep back in the objects vector. objects.assign(not_in_static_mask.begin(),not_in_static_mask.end()); } catch(...){ //if we encounter a problem, put all the elements we've kept and all those we haven't looked at yet back into the object vector. objects.assign(not_in_static_mask.begin(),not_in_static_mask.end()); for (vector<ns_detected_object *>::iterator p = objects_to_sort.begin(); p != objects_to_sort.end(); ++p){ if (*p == 0) continue; objects.push_back(*p); } } }
void ns_spine_drawer::draw_spine(const ns_image_standard & img, const ns_segment_cluster & seg, const ns_worm_shape & worm, ns_svg & svg){ //draw grayscale svg.draw_rectangle(ns_vector_2d(0,0),ns_vector_2d(img.properties().width,img.properties().height)*svg_dx,ns_color_8(0,0,0),ns_color_8(0,0,0),1,false); svg.start_group(); for (unsigned int y = 0; y < img.properties().height; y++) for (unsigned int x = 0; x < img.properties().width; x++) if (img[y][x]!=0) svg.draw_rectangle(ns_vector_2d(svg_dx*x,svg_dx*y),ns_vector_2d(svg_dx*(x+1),svg_dx*(y+1)),ns_color_8(img[y][x],img[y][x],img[y][x]), ns_color_8(img[y][x],img[y][x],img[y][x]),1,false); svg.end_group(); ns_vector_2d vertex[2]; for(unsigned int i = 0; i < seg.segments.size(); i++){ if (seg.segments.size() == 0)continue; vector<ns_vector_2d> points(seg.segments[i]->nodes.size()); for (unsigned int j = 0; j < seg.segments[i]->nodes.size(); j++) points[j] = seg.segments[i]->nodes[j].position*svg_dx; svg.draw_poly_line(points,ns_rainbow<ns_color_8>(((float)i+1)/(float)(seg.segments.size()+1))*.8); } unsigned int end_d(ns_worm_detection_constants::get(ns_worm_detection_constant::worm_end_node_margin,3200)); //draw spines ns_color_8 end_color_offset(30,10,10); ns_color_8 shadow_offset(30,30,30); ns_vector_2i offset(1,0); ns_color_8 color = ns_rainbow<ns_color_8>(0,(float).05), shadow = ns_color_8::safe_subtraction(color,shadow_offset); if (worm.nodes.size() != 0){ vector<ns_vector_2d> points(worm.nodes.size()); for (unsigned int i = 0; i < worm.nodes.size(); i++) points[i] = worm.nodes[i]*svg_dx; svg.draw_poly_line(points,color); } }
void ns_spine_drawer::draw_normals(const ns_worm_shape & worm, ns_image_standard & output, const unsigned int resize_factor){ const unsigned int o(ns_worm_detection_constants::get(ns_worm_detection_constant::spine_visualization_output_resolution,output.properties().resolution)); for (unsigned int j = 0; j < worm.nodes.size(); j+=o){ ns_vector_2i nv[2] = { ns_vector_2i( (int)(worm.normal_0[j].x*resize_factor), (int)(worm.normal_0[j].y*resize_factor)), ns_vector_2i( (int)(worm.normal_1[j].x*resize_factor), (int)(worm.normal_1[j].y*resize_factor)) }; if (nv[0] == ns_vector_2i(0,0) || nv[1] == ns_vector_2i(0,0)) continue; ns_vector_2i c((int)(worm.nodes[j].x*resize_factor), (int)(worm.nodes[j].y*resize_factor)); output.draw_line_color(c,nv[0]+c, ns_color_8(250,250,250)); output.draw_line_color(c,nv[1]+c, ns_color_8(250,250,250)); } }
string ns_barcode_decode_done(const ns_image_standard & im, const string & debug_image_filename){ //ns_save_image("c:\\bar_debug.tif",im); unsigned long c(im.properties().components); unsigned long w(im.properties().width); unsigned long h(im.properties().height); DmtxImage *image(dmtxImageMalloc(im.properties().width, im.properties().height)); try{ if (image == NULL) throw ns_ex("Could not persuade dmtxlib to allocate an image of dimentions ") << im.properties().width <<"x" << im.properties().height; if (image->height != im.properties().height || image->width != im.properties().width){ throw ns_ex("Requested an image of dimentions ") << im.properties().width <<"x" << im.properties().height << "; dmtx produced an image of dimentions " << image->width << "x" << image->height; } for (unsigned int y = 0; y < h; y++) for (unsigned int x = 0; x < w; x++){ image->pxl[y*w+x][0] = im[y][c*x]; image->pxl[y*w+x][1] = im[y][c*x]; image->pxl[y*w+x][2] = im[y][c*x]; } string ret = run_dmtx_decode(image); if (ret.size() != 0) return ret; //if we can't find a barcode, check to see if there has been a mirror-image reflection. for (unsigned int y = 0; y < h; y++) for (unsigned int x = 0; x < w; x++){ image->pxl[y*w+x][0] = im[y][c*(w-x-1)]; image->pxl[y*w+x][1] = im[y][c*(w-x-1)]; image->pxl[y*w+x][2] = im[y][c*(w-x-1)]; } string name(run_dmtx_decode(image)); delete image; return name; } catch(...){ delete image; throw; } }
void ns_out_frame_count(ns_image_standard & im, const unsigned long number_of_frames_used_to_find_stationary_objects, const unsigned long frame_count){ ns_xml_simple_writer xml; xml.add_header(); xml.add_tag("frame_count",frame_count); xml.add_tag("number_of_frames_used_to_find_stationary_objects",number_of_frames_used_to_find_stationary_objects); xml.add_footer(); im.set_description(xml.result()); /* ns_32_bit time_points_l = (ns_32_bit)frame_count; if ((ns_32_bit)frame_count > ((ns_32_bit)0)-1) frame_count = ((ns_32_bit)0)-1; ns_8_bit * tp = reinterpret_cast<ns_8_bit *>(&time_points_l); //place number of frames used to calculate heat map in bitmap im[0][0] = tp[0]; im[0][1] = tp[1]; im[0][2] = tp[2]; im[0][3] = tp[3]; im[0][4] = tp[0]; im[0][5] = tp[1];*/ }
void ns_movement_visualization_generator::create_time_path_analysis_visualization(const ns_image_server_captured_image_region & region_image, const ns_death_time_annotation_compiler_region & compiler_region,const ns_image_standard & grayscale, ns_image_standard & out,ns_sql & sql){ unsigned long thickness = 4; const ns_color_8 excluded_color = ns_color_8(50,50,255); ns_image_properties prop(grayscale.properties()); prop.components = 3; out.init(prop); for (unsigned int y = 0; y < prop.height; ++y){ for (unsigned long x = 0; x < prop.width; ++x){ out[y][3*x+0] = 255-grayscale[y][x]; out[y][3*x+1] = 255-grayscale[y][x]; out[y][3*x+2] = 255-grayscale[y][x]; } } /* ns_death_time_annotation_set movement_annotations; // movement_annotations.events.reserve(death_time_annotations.size()); // for (unsigned long i = 0; i < death_time_annotations.size(); i++){ switch(death_time_annotations[i].type){ case ns_fast_moving_worm_observed: case ns_slow_moving_worm_observed: case ns_posture_changing_worm_observed: case ns_stationary_worm_observed: movement_annotations.push_back(death_time_annotations[i]); } } */ ns_image_worm_detection_results results; results.id = region_image.region_detection_results_id; results.load_from_db(false,false,sql); ns_image_server_captured_image_region region_t(region_image); results.load_images_from_db(region_t,sql); const std::vector<const ns_detected_worm_info *> detected_worms(results.actual_worm_list()); ns_font & font(font_server.default_font()); const unsigned long font_height(25); font.set_height(font_height); std::vector<const ns_death_time_annotation *> representative_state_event_for_location(compiler_region.locations.size(),0); for (unsigned int i = 0; i < compiler_region.locations.size(); i++){ for (unsigned int j = 0; j < compiler_region.locations[i].annotations.size(); j++){ if (ns_movement_event_is_a_state_observation(compiler_region.locations[i].annotations[j].type) && compiler_region.locations[i].annotations[j].time.period_end == region_image.capture_time){ if (representative_state_event_for_location[i] != 0){ cerr << "Found multiple state events for a time!\n"; } else representative_state_event_for_location[i] = &compiler_region.locations[i].annotations[j]; } } } std::vector<const ns_death_time_annotation *> fast_moving_animal_matches(detected_worms.size(),0); for (unsigned int i = 0; i < compiler_region.fast_moving_animals.size(); i++){ for (unsigned long w = 0; w < detected_worms.size(); w++){ if (detected_worms[w]->region_size == compiler_region.fast_moving_animals[i].size && detected_worms[w]->region_position_in_source_image == compiler_region.fast_moving_animals[i].position){ fast_moving_animal_matches[w] = &compiler_region.fast_moving_animals[i]; } } } //for debugging std::vector<char> locations_matched(representative_state_event_for_location.size(),0); unsigned long locations_with_matches(0); //for real use std::vector<ns_death_time_annotation_compiler_region::ns_location_list::const_iterator> location_matches(detected_worms.size(),compiler_region.locations.end()); unsigned long unmatched_detected_worms(0); for (unsigned long w = 0; w < detected_worms.size(); w++){ for (unsigned int i = 0; i < representative_state_event_for_location.size(); i++){ if (representative_state_event_for_location[i] == 0) continue; if (detected_worms[w]->region_size == representative_state_event_for_location[i]->size && detected_worms[w]->region_position_in_source_image == representative_state_event_for_location[i]->position){ if(location_matches[w] != compiler_region.locations.end()){ cerr << "Found multiple locations that match detected worm!\n"; } else{ //for debugging locations_matched[i] = 1; locations_with_matches++; //for real use location_matches[w] = compiler_region.locations.begin()+i; } } } if (fast_moving_animal_matches[w] == 0 && location_matches[w]== compiler_region.locations.end()) unmatched_detected_worms++; } if (unmatched_detected_worms > 0) cerr << "Could not match up " << unmatched_detected_worms << " of " << detected_worms.size() << " animals.\n"; for (unsigned long w = 0; w < detected_worms.size(); w++){ const ns_death_time_annotation_compiler_region::ns_location_list::const_iterator location(location_matches[w]); const ns_death_time_annotation * fast_animal_match(fast_moving_animal_matches[w]); const ns_vector_2i & pos = detected_worms[w]->region_position_in_source_image; //if we can't find info on the object, paint it white. if (fast_animal_match == 0 && location== compiler_region.locations.end()){ const ns_color_8 color(255,255,255); ns_vector_2i size = detected_worms[w]->region_size; out.draw_line_color_thick(pos,pos+ns_vector_2i(size.x,0),color,3); out.draw_line_color_thick(pos,pos+ns_vector_2i(0,size.y),color,3); out.draw_line_color_thick(pos+ns_vector_2i(0,size.y),pos+size,color,3); out.draw_line_color_thick(pos+ns_vector_2i(size.x,0),pos+size,color,3); continue; } ns_color_8 color; if (location != compiler_region.locations.end()){ color = (ns_movement_colors::color(ns_movement_event_state(representative_state_event_for_location[location-compiler_region.locations.begin()]->type))); if (location->properties.is_excluded()) color = excluded_color; } else color = ns_movement_colors::color(ns_movement_fast); for (unsigned int y = 0; y < detected_worms[w]->bitmap().properties().height; y++){ for (unsigned int x = 0; x < detected_worms[w]->bitmap().properties().width; x++){ if (detected_worms[w]->bitmap()[y][x]){ out[pos.y + y][3*(pos.x + x)+0] = (ns_8_bit)(.75*color.x + .25*out[pos.y + y][3*(pos.x + x)+0]); out[pos.y + y][3*(pos.x + x)+1] = (ns_8_bit)(.75*color.y + .25*out[pos.y + y][3*(pos.x + x)+1]); out[pos.y + y][3*(pos.x + x)+2] = (ns_8_bit)(.75*color.z + .25*out[pos.y + y][3*(pos.x + x)+2]); } } } ns_color_8 edge_color(color); if (location != compiler_region.locations.end() && location->properties.number_of_worms_at_location_marked_by_hand > 1){ // font.draw_color(pos.x + (detected_worms[w]->region_size.x*3)/4,pos.y+font_height,ns_color_8(255,255,255),std::string("") + // ns_to_string(location->properties.number_of_worms_at_location_marked_by_hand),out); edge_color = ns_color_8(255,180,120); const int edge_width(3); // use signed ints for x and y so that "x + dx" type expressions are also properly signed. for (int y = 0; y < detected_worms[w]->edge_bitmap().properties().height; y++){ for (int x = 0; x < detected_worms[w]->edge_bitmap().properties().width; x++){ if (detected_worms[w]->edge_bitmap()[y][x]){ for (int dx = -edge_width; dx <= edge_width; dx++) for (int dy = -edge_width; dy <= edge_width; dy++){ if ( dx + dy > edge_width) continue; //round edges if (x + dx < 0 || x + dx >= detected_worms[w]->edge_bitmap().properties().width) continue; if (y + dy < 0 || y + dy >= detected_worms[w]->edge_bitmap().properties().height) continue; out[pos.y + y+dy][3*(pos.x + x+dx)+0] = (ns_8_bit)(.2*(ns_8_bit)(edge_color.x) + .8*out[pos.y + y+dy][3*(pos.x + x+dx)+0]); out[pos.y + y+dy][3*(pos.x + x+dx)+1] = (ns_8_bit)(.2*(ns_8_bit)(edge_color.y) + .8*out[pos.y + y+dy][3*(pos.x + x+dx)+1]); out[pos.y + y+dy][3*(pos.x + x+dx)+2] = (ns_8_bit)(.2*(ns_8_bit)(edge_color.z) + .8*out[pos.y + y+dy][3*(pos.x + x+dx)+2]); } } } } } } for (unsigned int i = 0; i < compiler_region.locations.size(); i++){ if (representative_state_event_for_location[i] == 0 || locations_matched[i]) continue; const ns_death_time_annotation & a(*representative_state_event_for_location[i]); if (compiler_region.locations[i].properties.inferred_animal_location){ ns_color_8 color = ns_movement_colors::color(ns_movement_event_state(a.type)); if (compiler_region.locations[i].properties.is_excluded()) color = excluded_color; unsigned long thickness = 4; out.draw_line_color_thick(a.position,a.position + ns_vector_2i(a.size.x,0),color,thickness,.8); out.draw_line_color_thick(a.position,a.position + ns_vector_2i(0,a.size.y),color,thickness,.8); out.draw_line_color_thick(a.position+ns_vector_2i(a.size.x,0),a.position + ns_vector_2i(a.size.x,a.size.y),color,thickness,.6); out.draw_line_color_thick(a.position+ns_vector_2i(0,a.size.y),a.position + ns_vector_2i(a.size.x,a.size.y),color,thickness,.6); } } }
void ns_movement_visualization_generator::create_survival_curve_for_capture_time(const long marker_time_t, const ns_region_metadata & metadata, const ns_survival_data_with_censoring & plate, const ns_survival_data_with_censoring & strain, const std::vector<unsigned long > & plate_time, const std::vector<unsigned long> & strain_time,const std::string & title, const bool draw_dark,const bool optimize_for_small_graph, ns_image_standard & image,ns_graph & graph) const{ //if (!data.set_is_on_common_time()) // throw ns_ex("ns_movement_visualization_generator::create_survival_curve_for_capture_time()::requires data to be on a common time set"); unsigned long plate_last_death(0),strain_last_death(0); for (unsigned int i = 0; i < plate.data.number_of_animals_at_risk.size(); i++){ plate_last_death = i; if (plate.data.number_of_animals_at_risk[i] == 0) break; } for (unsigned int i = 0; i < strain.data.number_of_animals_at_risk.size(); i++){ strain_last_death = i; if (strain.data.number_of_animals_at_risk[i] == 0) break; } if (plate_time.size() == 0) throw ns_ex("ns_movement_visualization_generator::create_survival_curve_for_capture_time()::The supplied plate time vector is empty"); unsigned long latest_time(plate_time[plate_last_death]); if (!strain_time.empty()) if (latest_time < strain_time[strain_last_death]) latest_time = strain_time[strain_last_death]; unsigned long marker_time(marker_time_t); if (marker_time > latest_time) marker_time = latest_time; ns_graph_object plate_survival(ns_graph_object::ns_graph_dependant_variable), strain_survival(ns_graph_object::ns_graph_dependant_variable), plate_marker(ns_graph_object::ns_graph_dependant_variable), strain_marker(ns_graph_object::ns_graph_dependant_variable), censored_markers(ns_graph_object::ns_graph_dependant_variable); plate_survival.y.resize(plate_last_death+1+1); plate_survival.x.resize(plate_last_death+1+1); strain_survival.y.resize(strain_last_death+1+1,0); strain_survival.x.resize(strain_last_death+1+1,0); //plate_marker.y.resize(plate_time.size()+1,-1); //plate_marker.x.resize(plate_time.size()+1,-1); //strain_marker.y.resize(strain_time.size()+1,-1); //strain_marker.x.resize(strain_time.size()+1,-1); unsigned long censored_size = plate_last_death; if (censored_size < strain_last_death) censored_size = strain_last_death; censored_markers.y.resize(censored_size+1+1,-1); censored_markers.x.resize(censored_size+1+1,-1); plate_marker.y.resize(1,-1); plate_marker.x.resize(1,-1); strain_marker.y.resize(1,-1); strain_marker.x.resize(1,-1); unsigned long plate_marker_index(0), strain_marker_index(0); for (unsigned int i = 0; i <= plate_last_death; i++){ if (marker_time >= plate_time[i]){ plate_marker_index = i; } else break; } for (unsigned int i = 0; i < strain_last_death; i++){ if (marker_time >= strain_time[i]){ strain_marker_index = i; } else break; } const unsigned long total_strain_deaths(strain.data.total_number_of_deaths), total_plate_deaths(plate.data.total_number_of_deaths), total_strain_censored(strain.data.total_number_of_censoring_events), total_plate_censored(plate.data.total_number_of_censoring_events), number_of_strain_deaths((strain_marker_index<strain_time.size())?strain.data.cumulative_number_of_deaths[strain_marker_index]:0), number_of_plate_deaths((plate_marker_index<plate_time.size())?plate.data.cumulative_number_of_deaths[plate_marker_index]:0); const double plate_fraction_surviving((plate_marker_index<plate_time.size())?plate.data.probability_of_surviving_up_to_interval[plate_marker_index]:0), strain_fraction_surviving((strain_marker_index<strain_time.size())?strain.data.probability_of_surviving_up_to_interval[strain_marker_index]:0); unsigned long number_of_strain_censored(0), number_of_plate_censored(0); if (strain_marker_index<= strain_last_death && !strain_time.empty()){ for (unsigned int i = 0; i <= strain_marker_index; i++) number_of_strain_censored+=strain.data.number_of_censoring_events[i]; } if (plate_marker_index<= plate_last_death){ for (unsigned int i = 0; i <= plate_marker_index; i++) number_of_plate_censored+=plate.data.number_of_censoring_events[i]; } plate_survival.y[0] = strain_survival.y[0] = 1; censored_markers.x[0] = plate_marker.x[0] = strain_marker.x[0] = plate_survival.x[0] = strain_survival.x[0] = 0; for (unsigned int i = 0; i < plate_survival.y.size()-1; i++){ plate_survival.y[i+1] = plate.data.probability_of_surviving_up_to_interval[i]; if (plate_survival.y[i] <= 0) plate_survival.y[i+1] = -1; plate_survival.x[i+1] = (plate_time[i]-metadata.time_at_which_animals_had_zero_age)/60.0/60.0/24.0; plate_marker.x[0] = (marker_time-metadata.time_at_which_animals_had_zero_age)/60.0/60.0/24.0; censored_markers.x[i+1] = (plate_time[i]-metadata.time_at_which_animals_had_zero_age)/60.0/60.0/24.0; if (i == plate_marker_index) plate_marker.y[0] = plate.data.probability_of_surviving_up_to_interval[i]; } if (!strain_time.empty()) for (unsigned int i = 0; i < strain_survival.y.size()-1; i++){ strain_survival.y[i+1] = strain.data.probability_of_surviving_up_to_interval[i]; if (strain_survival.y[i] <= 0) strain_survival.y[i+1] = -1; strain_survival.x[i+1] = (strain_time[i]-metadata.time_at_which_animals_had_zero_age)/60.0/60.0/24.0; strain_marker.x[0] = (marker_time-metadata.time_at_which_animals_had_zero_age)/60.0/60.0/24.0; if (i == strain_marker_index) strain_marker.y[0] = strain.data.probability_of_surviving_up_to_interval[i]; } //for (unsigned int i = 0; i < strain_survival.y.size()-1; i++){ // if (strain.data.number_of_censoring_events[i] > 0) // censored_markers.y[i+1] = strain.data.probability_of_surviving_up_to_interval[i]; //} //kaplan meyer plots show the number of individuals left alive up to the time point measured. Thus the marker should //be at the bottom of the vertical steps on the plot. plate_survival.properties.point.draw = false; plate_survival.properties.line.draw = true; plate_survival.properties.line.width = 4; plate_survival.properties.line_hold_order = ns_graph_properties::ns_zeroth; plate_survival.properties.draw_vertical_lines = ns_graph_properties::ns_outline; plate_survival.properties.draw_negatives = false; graph.x_axis_properties.text_decimal_places = 0; graph.y_axis_properties.text_decimal_places = 0; if (draw_dark){ graph.x_axis_properties.line.color=ns_color_8(255,255,255); graph.x_axis_properties.text.color=ns_color_8(255,255,255); graph.x_axis_properties.text.draw = true; graph.x_axis_properties.point.color=ns_color_8(255,255,255); graph.x_axis_properties.area_fill.color=ns_color_8(0,0,0); graph.y_axis_properties = graph.area_properties = graph.title_properties = graph.x_axis_properties; } if (optimize_for_small_graph){ graph.x_axis_properties.text_size*=4; graph.y_axis_properties.text_size*=4; graph.area_properties.text_size*=4; graph.x_axis_properties.line.width*=4; graph.y_axis_properties.line.width*=4; graph.area_properties.line.width*=4; plate_survival.properties.line.width*=4; } strain_survival.properties = plate_survival.properties; if (draw_dark){ plate_survival.properties.line.color=ns_color_8(255,255,0); strain_survival.properties.line.color=ns_color_8(200,0,0); } else{ plate_survival.properties.line.color=ns_color_8(0,0,0); strain_survival.properties.line.color=ns_color_8(50,0,0); } plate_marker.properties = plate_survival.properties; plate_marker.properties.line.draw = 0; plate_marker.properties.area_fill.draw = 0; plate_marker.properties.point.draw = true; plate_marker.properties.point.color = plate_marker.properties.area_fill.color; plate_marker.properties.point.width = 15 + 10*(int)optimize_for_small_graph; plate_marker.properties.point.edge_width = plate_marker.properties.point.width/3; plate_marker.properties.point.edge_color = ns_color_8(255,255,255); plate_marker.properties.draw_negatives = false; censored_markers.properties = strain_marker.properties = plate_marker.properties; censored_markers.properties.point.color = plate_survival.properties.line.color; censored_markers.properties.point.point_shape = ns_graph_color_set::ns_vertical_line; ns_graph_object graph_x_axis(ns_graph_object::ns_graph_independant_variable); /* unsigned long index_at_which_all_plate_animals_are_dead(strain_survival.y.size()); for (unsigned int i = 0; i < strain_survival.y.size(); i++){ if (strain_survival.y[i] < .001){ index_at_which_all_plate_animals_are_dead = i; } } index_at_which_all_plate_animals_are_dead=(5*index_at_which_all_plate_animals_are_dead)/4; if (index_at_which_all_plate_animals_are_dead>strain_survival.y.size()) index_at_which_all_plate_animals_are_dead = strain_survival.y.size(); graph_x_axis.x.resize(index_at_which_all_plate_animals_are_dead ); strain_survival.y.resize(index_at_which_all_plate_animals_are_dead ); strain_marker.y.resize(index_at_which_all_plate_animals_are_dead ); plate_survival.y.resize(index_at_which_all_plate_animals_are_dead ); plate_marker.y.resize(index_at_which_all_plate_animals_are_dead ); */ // graph.contents.push_back(graph_x_axis); graph.contents.push_back(censored_markers); if (!strain_time.empty()){ graph.contents.push_back(strain_survival); graph.contents.push_back(strain_marker); } graph.contents.push_back(plate_survival); graph.contents.push_back(plate_marker); if (image.properties().height > 0){ const unsigned long number_of_lines(7); ns_font & font(font_server.default_font()); font.set_height(image.properties().height/ number_of_lines); for (unsigned int y = 0; y < image.properties().height; y++){ for (unsigned int x = 0; x < 3*image.properties().width; x++){ image[y][x] = 0; } } string text; text = "Age: "; const unsigned long seconds_since_birth(marker_time - metadata.time_at_which_animals_had_zero_age); float age = seconds_since_birth/(24*60*60.0); string units(" days"); if (seconds_since_birth < 4*24*60*60){ age = seconds_since_birth/(60*60.0); units = " hours"; } unsigned long line_num(1); text += ns_to_string_short(age,2) + units; font.draw_color(8,line_num*image.properties().height/(number_of_lines-1),ns_color_8(255,255,255),text,image); line_num++; text = "Plate Survival: "; text += ns_to_string_short(plate_fraction_surviving,2); font.draw_color(8,line_num*image.properties().height/(number_of_lines-1),ns_color_8(255,255,255),text,image); line_num++; text = "Plate Dead: "; text += ns_to_string( number_of_strain_deaths); text += "/"; text += ns_to_string( total_strain_deaths); font.draw_color(8,line_num*image.properties().height/(number_of_lines-1),ns_color_8(255,255,255),text,image); line_num++; text = "Plate Censored: "; text += ns_to_string( number_of_strain_censored); text += "/"; text += ns_to_string( total_strain_censored); font.draw_color(8,line_num*image.properties().height/(number_of_lines-1),ns_color_8(255,255,255),text,image); line_num++; if (!strain_time.empty()){ text = "Strain Survival: "; text += ns_to_string_short(strain_fraction_surviving,2); font.draw_color(8,line_num*image.properties().height/(number_of_lines-1),ns_color_8(255,255,255),text,image); } } }
//A heat map is generated by summing all thresholded frames of a time series. For the first //third of the time series, treshold values are added to the red channel of the heat map. //for the second third of the time series, threshold values are added to the green channel //for the final, blue. //This means we can roughly inspect which pixels are constantly bright for the biggining, middle, or end //of the experiment. //This function looks for pixels that were bright for a specified fraction of each third of the experiment. //Such bright pixels, if situated in a region where at least one pixel was bright in the two other thirds //of the experiment, are added to the output image. //The output image can then be used to mask out unwanted "static" pixels that represent //features such as the plate edge or dust. void ns_worm_multi_frame_interpolation::generate_static_mask_from_heatmap(const ns_image_standard & im, ns_image_standard & out){ const unsigned int spatial_smudge_distance(ns_worm_detection_constants::get(ns_worm_detection_constant::allowed_drift_distance_for_objects_during_static_mask_creation)), early_strong_threshold(ns_worm_detection_constants::get(ns_worm_detection_constant::proportion_of_early_time_points_present_required_during_static_mask_creation)), //out of 10 middle_threshold(ns_worm_detection_constants::get(ns_worm_detection_constant::proportion_of_middle_time_points_present_required_during_static_mask_creation)), //out of 10 late_threshold(ns_worm_detection_constants::get(ns_worm_detection_constant::proportion_of_late_time_points_present_required_during_static_mask_creation)); //out of 10 if (im.properties().width < 2 || im.properties().height == 0) throw ns_ex("ns_worm_multi_frame_interpolation::Empty heat map was provided"); ns_32_bit number_of_frames_used_to_make_heatmap(0); ns_32_bit number_of_frames_used_to_find_stationary_objects(0); ns_32_bit time_points_l = 0; if (im.properties().description.size() != 0){ //load frame information from xml spec bool found_total(false), found_begin_count(false); ns_xml_simple_object_reader xml; xml.from_string(im.properties().description); for (unsigned int i = 0; i < xml.objects.size(); i++){ if (xml.objects[i].name == "frame_count"){ number_of_frames_used_to_make_heatmap = atol(xml.objects[i].value.c_str()); found_total = true; } else if (xml.objects[i].name =="number_of_frames_used_to_find_stationary_objects"){ number_of_frames_used_to_find_stationary_objects = atol(xml.objects[i].value.c_str()); found_begin_count = true; } } if (!found_total) throw ns_ex("ns_worm_multi_frame_interpolation::generate_static_mask_from_heatmap::Could not find total frame count specification in heat map"); if (!found_begin_count) throw ns_ex("ns_worm_multi_frame_interpolation::generate_static_mask_from_heatmap::Could not find number_of_frames_used_to_find_stationary_objects specification in heat map"); } else{ throw ns_ex("Old style heat maps no longer supported!"); //old style heatmaps stored pixel information in the first five pixels of the image. ns_8_bit a=im[0][0], b=im[0][1], c=im[0][2], d=im[0][3], e=im[0][4], f=im[0][5]; if (im[0][0] != im[0][4] || im[0][1] != im[0][5]) throw ns_ex("ns_worm_multi_frame_interpolation::Could not find watermark specifiying source frame count"); ns_8_bit * fr = reinterpret_cast<ns_8_bit *>(&number_of_frames_used_to_make_heatmap); fr[0] = im[0][0]; fr[1] = im[0][1]; fr[2] = im[0][2]; fr[3] = im[0][3]; if (number_of_frames_used_to_make_heatmap < 30) number_of_frames_used_to_find_stationary_objects = number_of_frames_used_to_make_heatmap; else if (number_of_frames_used_to_make_heatmap < 90) number_of_frames_used_to_find_stationary_objects = number_of_frames_used_to_make_heatmap/3; number_of_frames_used_to_find_stationary_objects = number_of_frames_used_to_make_heatmap/9; } //number_of_frames_used_to_make_heatmap = 306; ns_number_of_frames_in_heatmap number_of_frames(number_of_frames_used_to_find_stationary_objects,number_of_frames_used_to_make_heatmap); /*cerr << number_of_frames_used_to_make_heatmap << " frames used in heat map, divided into " << number_of_frames.early << ", "<< number_of_frames.middle << ", and " << number_of_frames.late << "\n"; */ if (im.properties().components != 3) throw ns_ex("ns_image_server_calculate_heatmap_overlay::Heatmaps must be in color"); ns_image_properties p(im.properties()); p.components = 1; out.prepare_to_recieve_image(p); for (int y = 0; y < (int)p.height; y++){ for (int x = 0; x < (int)p.width; x++){ const bool found_early(10*((unsigned int)im[y][3*x ]) >= early_strong_threshold*number_of_frames.early), found_middle(10*((unsigned int)im[y][3*x+1]) >= middle_threshold*number_of_frames.middle), found_late(10*((unsigned int)im[y][3*x+2]) >= late_threshold*number_of_frames.late); if(!found_early &&!found_middle &&!found_late){ out[y][x] = 0; continue; } int x0(x-spatial_smudge_distance), x1(x+spatial_smudge_distance), y0(y-spatial_smudge_distance), y1(y+spatial_smudge_distance); if (x0 < 0) x0 = 0; if (x1 >= (int)p.width) x1 = (int)p.width-1; if (y0 < 0) y0 = 0; if (y1 >= (int)p.height) y1 = (int)p.height-1; bool cont(true); out[y][x] = 0; for (int _y = y0; _y < y1 && cont; _y++){ for (int _x = x0; _x < x1 && cont; _x++){ if( 10*((unsigned int)im[_y][3*_x ]) >= early_strong_threshold*number_of_frames.early || (found_early && (10*((unsigned int)im[_y][3*_x+1]) >= middle_threshold*number_of_frames.middle || 10*((unsigned int)im[_y][3*_x+2]) >= late_threshold*number_of_frames.late) ) ){ out[y][x] = 255; cont = false; } } } } } }
void ns_worm_multi_frame_interpolation::generate_heat_map(ns_image_standard & heat_map_out,const unsigned int number_of_frames_used_to_find_stationary_objects_,ns_sql & sql){ if (time_points.size() == 0) throw ns_ex("ns_worm_multi_frame_interpolation::generate_heat_map::No time points available!"); unsigned long number_of_frames_used_to_find_stationary_objects(number_of_frames_used_to_find_stationary_objects_); //0 indicates that the algorithm should choose the best number of points to use if (time_points.size() <= 2 && number_of_frames_used_to_find_stationary_objects != 1) throw ns_ex("Cannot calculate static mask for such a small number of frames:" ) << time_points.size(); //if not stationary object frame count is specified, determine one automatically if (number_of_frames_used_to_find_stationary_objects_ == 0){ //for very short experiments (ie heat shock) use a short time interval if (time_points.size() < 60) number_of_frames_used_to_find_stationary_objects = 3; else{ if (number_of_frames_used_to_find_stationary_objects_ > time_points.size()) number_of_frames_used_to_find_stationary_objects = time_points.size()/5; if (number_of_frames_used_to_find_stationary_objects==0) number_of_frames_used_to_find_stationary_objects = 2; } } //get image size for heatmap from first threshold bool size_loaded = false; ns_image_properties heat_map_size; for (unsigned int i = 0; i < time_points.size(); i++){ try{ //cerr << "Loading heat_map size from time_point " << i << "\n"; time_points[i]->load_threshold(sql); heat_map_size = time_points[i]->threshold.properties(); size_loaded = true; break; } catch(ns_ex & ex){ cerr << "Could not open threshold frame for size info: " << ex.text() << "\n"; } } if (!size_loaded) throw ns_ex("ns_worm_multi_frame_interpolation::generate_heat_map()::Sample region has no usable images!"); heat_map_size.components = 3; heat_map_out.prepare_to_recieve_image(heat_map_size); for (unsigned int y = 0; y < heat_map_size.height; y++) for (unsigned int x = 0; x < 3*heat_map_size.width; x++) heat_map_out[y][x] = 0; unsigned int i = 0; for (std::vector<ns_worm_interpolation_timepoint *>::iterator p = time_points.begin();;){ //stop at one element before the end. std::vector<ns_worm_interpolation_timepoint *>::iterator end_p = time_points.end(); end_p--; if (p == end_p) break; try{ cerr << "Processing frame " << i+1 << "/" << time_points.size() << "\n"; if (i != 0) (*p)->load_threshold(sql); } catch(ns_ex & ex){ cerr << ex.text() << "\n"; p = time_points.erase(p); continue; } for (long y = 0; y < (long)(*p)->threshold.properties().height; y++){ for (long x = 0; x < (long)(*p)->threshold.properties().width; x++){ long color = ns_get_division_from_frame_number(i,number_of_frames_used_to_find_stationary_objects,(unsigned int)time_points.size()); if ((*p)->threshold[y][x] && heat_map_out[y][3*x+color] < 255) heat_map_out[y][3*x+color]++; } } (*p)->clear_threshold(); p++; i++; } ns_out_frame_count(heat_map_out,number_of_frames_used_to_find_stationary_objects,(unsigned long)time_points.size()); }
void ns_barcode_encoder::encode(const string & str, ns_image_standard & image, const unsigned int margin_size){ #ifdef NS_USE_2D_SCANNER_BARCODES DmtxEncode encode; encode = dmtxEncodeStructInit(); unsigned char * a(new unsigned char[str.size()+1]); try{ for (unsigned int i = 0; i < str.size(); i++) a[i] = str[i]; a[str.size()] = 0; cerr << "Encoding " << a << "\n"; dmtxEncodeDataMatrix(&encode, (int)str.size(), a, DMTX_SYMBOL_SQUARE_AUTO); image.prepare_to_recieve_image(ns_image_properties(encode.image->height+2*margin_size, encode.image->width+2*margin_size, 1,150)); for (unsigned int y = 0; y < image.properties().height; y++) for (unsigned int x = 0; x < image.properties().width; x++) image[y][x] = 255; for (unsigned int y = margin_size; y < image.properties().height-margin_size; y++) for (unsigned int x = margin_size; x < image.properties().width-margin_size; x++) image[y][x] = encode.image->pxl[encode.image->width*(y-margin_size) + (x-margin_size)][0]; ns_font & font(font_server.default_font()); font.draw(margin_size+10,margin_size + encode.image->height + 10,ns_color_8(125,125,125),str,image); dmtxEncodeStructDeInit(&encode); delete[] a; } catch(...){ dmtxEncodeStructDeInit(&encode); delete[] a; throw; } #else const unsigned w[3] = {3,6,12}; const unsigned int spacing = w[1]; vector <unsigned int> bar_widths; for (unsigned int i = 0; i < str.size(); i++){ unsigned int v; if (str[i] == '_') v = 26; else v = str[i]-'a'; bar_widths.push_back( w[(v/9)%3]); bar_widths.push_back( w[(v/3)%3]); bar_widths.push_back( w[(v )%3]); } unsigned int total_width = 0; for(unsigned int i = 0; i < bar_widths.size(); i++) total_width += bar_widths[i] + spacing; ns_image_properties prop; prop.width = total_width + 2*margin_size; prop.height = height*2 + 2*margin_size; prop.components = 1; prop.resolution = 150; image.prepare_to_recieve_image(prop); for (unsigned int y = 0; y < prop.height; y++) for (unsigned int x = 0; x < prop.width; x++) image[y][x] = 255; unsigned int x = 0; for (unsigned int i = 0; i < bar_widths.size(); i++){ for (unsigned int y = 0; y < height; y++) for (unsigned int dx = 0; dx < bar_widths[i]; dx++) image[y+margin_size][x+margin_size+dx] = 0; x += bar_widths[i]; for (unsigned int y = 0; y < height; y++) for (unsigned int dx = 0; dx < spacing; dx++) image[y+margin_size][x+margin_size+dx] = 255; x += spacing; } font.draw(margin_size,height*3/2+margin_size,ns_color_8(0,0,0),str,image); #endif }
string ns_barcode_decode(const ns_image_standard & image, const string & debug_image_filename){ unsigned char c = image.properties().components; ns_image_properties p(image.properties()); /*ns_histogram<unsigned long,ns_8_bit> hist; for (unsigned int y = 0; y < p.height; y++){ for (unsigned int x = 0; x < p.width; x+=c){ hist[ image[y][x] ]++; } } unsigned int max,min; for (min = 0; min < hist.size(); min++) if (hist[min] != 0)break; for (max = hist.size(); max > 1; max--) if (hist[max] != 0)break;*/ unsigned char thresh = 125; //build the vertical intensity profile of the image //we're looking for the top and bottom of the barcode strip. vector<unsigned int> profile(p.height,0); for (unsigned int y = 0; y < p.height; y++) for (unsigned int x = 0; x < c*p.width; x+=c) profile[y]+=image[y][x] >= thresh; vector<unsigned int> profile_smoothed(p.height,0); ns_smooth_series<unsigned int, 48>(profile,profile_smoothed,0); int bottom(0), top((int)profile_smoothed.size()-1); for (int i = (int)profile_smoothed.size()/2; i >=0; i--){ if (profile[i] >= profile[bottom]) bottom = i; } for (int i = (int)profile_smoothed.size()/2; i < (int)profile_smoothed.size(); i++){ if (profile[i] >= profile[top]) top = i; } unsigned int b_old(bottom), t_old(top); if (b_old >= t_old) throw ns_ex("ns_barcode_decode::Could not register barcode (vertical)"); bottom = b_old;//bottom + (t_old-b_old)/4; top = t_old;//top - (t_old-b_old)/4; unsigned int b_marge = bottom + (t_old-b_old)/4, t_marge = top - (t_old-b_old)/4; //calculate median intensity of bright area ns_histogram<unsigned long,ns_8_bit> hist; for (int y= bottom; y < top; y++) for (unsigned int x = 0; x < p.width; x++) hist[image[y][c*x]]++; hist.set_number_of_pixels((top-bottom)*p.width); ns_8_bit bright = (ns_8_bit)((3*(unsigned int)hist.median_from_histogram())/5); //calculate 10th percentile unsigned int tenth(0); unsigned int tenth_count(0); for (tenth = 0; tenth< hist.size(); tenth++){ tenth_count+= hist[tenth] > 50; if (tenth_count > 10) break; } //bright = tenth; //find start of bright region at left unsigned int l_old(0),r_old(p.width); //find l_old edge for (unsigned int x = 0; x < p.width/2; x++){ unsigned int sum = 0; for (unsigned int y = b_marge; y < t_marge; y++) sum+= image[y][c*x]; if (sum >= bright*( t_marge-b_marge)){ l_old = x; break; } if (l_old != 0) break; } //find the end of the dark region (the barcode) at right unsigned int dark_cut = tenth*3*( t_marge-b_marge); for (int x = l_old; x < (int)p.width ; x++){ unsigned int sum = 0; for (unsigned int y = b_marge; y < t_marge; y++) sum+= image[y][c*x]; if (sum <= dark_cut){ r_old = x; } } //find r_old edge /*for (int x = (int)p.width-1; x > (int)p.width/2 ; x--){ unsigned int sum = 0; for (unsigned int y = b_marge; y < t_marge; y++) sum+= image[y][c*x]; if (sum >= bright*( t_marge-b_marge)){ r_old = x; break; } if (r_old != p.width) break; }*/ /*for (unsignj unsigned long window = p.width/12; int start = l_old - window; int stop = r_old + window; if (start < 0) start = window; if (stop > p.width) stop = p.width - window; vector<unsigned int> scores(stop-start); for (int x = start; x < stop; x++){ unsigned int vertical_mean(0); unsigned int vertical_varience(0); for (unsigned int y = bottom; y < top; y++) vertical_mean+=image[y][x+window]; vertical_mean/=((2*window+1)*(top-bottom)); }*/ unsigned int left = (11*l_old)/10, // right = r_old - ((11*(p.width-r_old))/10); right = r_old + (p.width-r_old)/25; if (left >= right) throw ns_ex("ns_barcode_decode::Could not register barcode (horizontal)"); string dbg2_filename; if (debug_image_filename.size() != 0){ ns_image_standard im; image.pump(im,1024); for (int y = bottom; y < top; y++){ im[y][left] = 255*(y%2); im[y][right] = 255*(y%2); } for (unsigned int x = left; x < right; x++){ im[bottom][x] = 255*(x%2); im[top][x] = 255*(x%2); } /* ns_image_storage_reciever_handle<ns_8_bit> processing_out(image_server.image_storage.request_volatile_storage(debug_image_filename,1024,false)); im.pump(processing_out.output_stream(),1024); dbg2_filename = ns_dir::extract_filename_without_extension(debug_image_filename) + "2." + ns_dir::extract_extension(debug_image_filename);*/ } ns_image_bitmap bmp; ns_image_properties bprop(p); bprop.components = 1; bprop.width = right-left; bprop.height = top-bottom; bmp.prepare_to_recieve_image(bprop); const unsigned int read_height(4); for (unsigned int y = 0; y < read_height; y++) for (unsigned int x = 0; x < bprop.width; x++) bmp[y][x] = 0; for (unsigned int y = read_height; y < bprop.height-read_height; y++){ for (unsigned int x = 0; x < bprop.width; x++){ const int b(y - read_height); const int t(y + read_height); int sum(0); for (int i = b; i < t; i++) sum+= image[i+bottom][c*left+c*x]; bmp[y][x] = (sum <= (t-b)*thresh); } } for (unsigned int y = 0; y < read_height; y++) for (unsigned int x = 0; x < bprop.width; x++) bmp[bprop.height-1-y][x] = 0; return ns_barcode_decode(bmp,dbg2_filename); }
void ns_spine_drawer::draw_spine(const ns_image_standard & img, const ns_segment_cluster & seg, const ns_worm_shape & worm, ns_image_standard & output, const unsigned int resize_factor){ ns_image_standard im2; im2.init(img.properties()); for (unsigned int y = 0; y < im2.properties().height; y++){ for (unsigned int x = 0; x < im2.properties().components*im2.properties().width; x++){ im2[y][x] = img[y][x]; } } ns_image_properties outprop = im2.properties(); outprop.height*=resize_factor; outprop.width*=resize_factor; outprop.resolution*=resize_factor; outprop.components = 3; //cerr << "Drawing spine RF:" << resize_factor << "(" << outprop.width << "," << outprop.height << ")\n"; output.init(outprop); //enlarge bitmap. if (im2.properties().components == 1){ //b&w image for (unsigned int y = 0; y < im2.properties().height; y++){ for (unsigned int x = 0; x < im2.properties().width; x++) for (unsigned int r_y = 0; r_y < resize_factor; r_y++) for (unsigned int r_x = 0; r_x < resize_factor; r_x++){ for (unsigned int c = 0; c < 3; c++) output[resize_factor*y + r_y][3*(resize_factor*x+r_x)+c] = im2[y][x]; #ifdef NS_DRAW_BITMAP_EDGES if (edge_bitmap[y][x]!=0){ output[resize_factor*y + r_y][3*(resize_factor*x+r_x) ] = 255; output[resize_factor*y + r_y][3*(resize_factor*x+r_x)+1] = 0; output[resize_factor*y + r_y][3*(resize_factor*x+r_x)+2] = 255; } #endif } } } else{ //color image for (unsigned int y = 0; y < im2.properties().height; y++){ for (unsigned int x = 0; x < im2.properties().width; x++) for (unsigned int r_y = 0; r_y < resize_factor; r_y++) for (unsigned int r_x = 0; r_x < resize_factor; r_x++) for (unsigned int c = 0; c < 3; c++) output[resize_factor*y + r_y][3*(resize_factor*x+r_x)+c] = im2[y][3*x+c]; } } #ifdef NS_DRAW_SPINE_NORMALS ns_color_8 dk_gray(60,70,80); #else ns_color_8 dk_gray(170,170,255); #endif ns_color_8 red(200,15,0); ns_color_8 yellow(255,255,30); ns_color_8 pink(200,0,200); //draw triangles //draw_mesh(mesh,dk_gray,resize_factor,output); ns_vector_2i vertex[2]; for(unsigned int i = 0; i < seg.segments.size(); i++){ if (seg.segments.size() == 0)continue; unsigned int j; vertex[0].x = (int)(seg.segments[i]->nodes[0].position.x * resize_factor); vertex[0].y = (int)(seg.segments[i]->nodes[0].position.y * resize_factor); for (j = 1; j < seg.segments[i]->nodes.size(); j++){ vertex[1].x = (int)(seg.segments[i]->nodes[j].position.x * resize_factor); vertex[1].y = (int)(seg.segments[i]->nodes[j].position.y * resize_factor); output.draw_line_color(vertex[0],vertex[1], ns_rainbow<ns_color_8>(((float)i+1)/(float)(seg.segments.size()+1))*.8); vertex[0] = vertex[1]; } } unsigned int end_d(ns_worm_detection_constants::get(ns_worm_detection_constant::worm_end_node_margin,im2.properties().resolution)); //draw spines ns_color_8 end_color_offset(30,10,10); ns_color_8 shadow_offset(30,30,30); ns_vector_2i offset(1,0); ns_color_8 color = ns_rainbow<ns_color_8>(0,(float).05), shadow = ns_color_8::safe_subtraction(color,shadow_offset); if (worm.nodes.size() != 0){ vertex[0].x = (int)(worm.nodes[0].x * resize_factor); vertex[0].y = (int)(worm.nodes[0].y * resize_factor); for (unsigned int i = 0; i < (unsigned int )worm.nodes.size(); i++){ vertex[1].x = (int)(worm.nodes[i].x * resize_factor); vertex[1].y = (int)(worm.nodes[i].y * resize_factor); if (i == 1 || i == worm.nodes.size() -1) output.draw_line_color(vertex[0],vertex[1], ns_color_8::safe_subtraction(color,end_color_offset),1); //draw endpoints a different color to allow loop disambiguation else output.draw_line_color(vertex[0], vertex[1],color,1); vertex[0] = vertex[1]; } } }