int main(int argc, char **argv) { CmdLineOptions args; try { TCLAP::CmdLine cmd("Peasoup - a GPU pulsar search pipeline", ' ', "1.0"); TCLAP::UnlabeledMultiArg<std::string> arg_multi("filterbanks","File names", true, "string", cmd); TCLAP::ValueArg<std::string> arg_samp_outfilename("", "o", "Sample mask output filename", false, "rfi.eb_mask", "string", cmd); TCLAP::ValueArg<std::string> arg_spec_outfilename("", "o2", "Birdie list output filename", false, "birdies.txt", "string", cmd); TCLAP::ValueArg<float> arg_boundary_5_freq("l", "boundary_5_freq", "Frequency at which to switch from median5 to median25", false, 0.05, "float", cmd); TCLAP::ValueArg<float> arg_boundary_25_freq("a", "boundary_25_freq", "Frequency at which to switch from median25 to median125", false, 0.5, "float", cmd); TCLAP::ValueArg<int> arg_nharmonics("n", "nharmonics", "Number of harmonic sums to perform", false, 4, "int", cmd); TCLAP::ValueArg<float> arg_threshold("", "thresh", "The S/N threshold for a candidate to be considered for coincidencing matching", false, 4.0, "float", cmd); TCLAP::ValueArg<int> arg_beam_threshold("", "beam_thresh", "The number of beams a candidate must appear in to be considered multibeam", false, 4, "int", cmd); TCLAP::ValueArg<float> arg_min_freq("L", "min_freq", "Lowest Fourier freqency to consider", false, 0.1, "float",cmd); TCLAP::ValueArg<float> arg_max_freq("H", "max_freq", "Highest Fourier freqency to consider", false, 1100.0, "float",cmd); TCLAP::ValueArg<int> arg_max_harm("b", "max_harm", "Maximum harmonic for related candidates", false, 16, "float",cmd); TCLAP::ValueArg<float> arg_freq_tol("f", "freq_tol", "Tolerance for distilling frequencies (0.0001 = 0.01%)", false, 0.0001, "float",cmd); TCLAP::SwitchArg arg_verbose("v", "verbose", "verbose mode", cmd); cmd.parse(argc, argv); args.multi = arg_multi.getValue(); args.samp_outfilename = arg_samp_outfilename.getValue(); args.spec_outfilename = arg_spec_outfilename.getValue(); args.boundary_5_freq = arg_boundary_5_freq.getValue(); args.boundary_25_freq = arg_boundary_25_freq.getValue(); args.nharmonics = arg_nharmonics.getValue(); args.threshold = arg_threshold.getValue(); args.beam_threshold = arg_beam_threshold.getValue(); args.min_freq = arg_min_freq.getValue(); args.max_freq = arg_max_freq.getValue(); args.freq_tol = arg_freq_tol.getValue(); args.verbose = arg_verbose.getValue(); }catch (TCLAP::ArgException &e) { std::cerr << "Error: " << e.error() << " for arg " << e.argId() << std::endl; return -1; } int ii; int nfiles = args.multi.size(); std::vector<DedispersedTimeSeries<unsigned char> > tims; for (ii=0;ii<nfiles;ii++){ if (args.verbose) std::cout << "Reading and dedispersing " << args.multi[ii] << std::endl; SigprocFilterbank filterbank(args.multi[ii]); Dedisperser dedisperser(filterbank,1); dedisperser.generate_dm_list(0.0,0.0,0.4,1.1); DispersionTrials<unsigned char> trial = dedisperser.dedisperse(); tims.push_back(trial[0]); } size_t size = tims[0].get_nsamps(); for (ii=0;ii<nfiles;ii++){ if (tims[ii].get_nsamps() != size) ErrorChecker::throw_error("Not all filterbanks the same length"); } float tsamp = tims[0].get_tsamp(); CuFFTerR2C r2cfft(size); CuFFTerC2R c2rfft(size); float tobs = size*tsamp; float bin_width = 1.0/tobs; std::vector< ReusableDeviceTimeSeries<float,unsigned char>* > d_tims; for (ii=0;ii<nfiles;ii++){ d_tims.push_back(new ReusableDeviceTimeSeries<float,unsigned char>(size)); } DeviceFourierSeries<cufftComplex> d_fseries(size/2+1,bin_width); std::vector< DevicePowerSpectrum<float>* > pspecs; for (ii=0;ii<nfiles;ii++){ pspecs.push_back(new DevicePowerSpectrum<float>(d_fseries)); } Dereddener rednoise(size/2+1); SpectrumFormer former; float mean,std,rms; for (ii=0;ii<nfiles;ii++){ if (args.verbose) std::cout << "Baselining beam " << ii << std::endl; d_tims[ii]->copy_from_host(tims[ii]); r2cfft.execute(d_tims[ii]->get_data(),d_fseries.get_data()); former.form(d_fseries,*pspecs[ii]); rednoise.calculate_median(*pspecs[ii]); rednoise.deredden(d_fseries); former.form_interpolated(d_fseries,*pspecs[ii]); stats::stats<float>(pspecs[ii]->get_data(),size/2+1,&mean,&rms,&std); stats::normalise(pspecs[ii]->get_data(),mean,std,size/2+1); c2rfft.execute(d_fseries.get_data(),d_tims[ii]->get_data()); stats::stats<float>(d_tims[ii]->get_data(),size,&mean,&rms,&std); stats::normalise(d_tims[ii]->get_data(),mean,std,size); } if (args.verbose) std::cout << "Performing cross beam coincidence matching" << std::endl; std::vector<float*> tim_host_ptr_array; std::vector<float*> spec_host_ptr_array; for (ii=0;ii<nfiles;ii++){ tim_host_ptr_array.push_back(d_tims[ii]->get_data()); spec_host_ptr_array.push_back(pspecs[ii]->get_data()); } DevicePowerSpectrum<float> spec_mask(d_fseries); DeviceTimeSeries<float> samp_mask(size); Coincidencer ccder(nfiles); ccder.match(&tim_host_ptr_array[0],samp_mask.get_data(), size,args.threshold,args.beam_threshold); ccder.match(&spec_host_ptr_array[0],spec_mask.get_data(), size/2+1,args.threshold,args.beam_threshold); ccder.write_samp_mask(samp_mask.get_data(),size, args.samp_outfilename); ccder.write_birdie_list(spec_mask.get_data(),size/2+1, spec_mask.get_bin_width(), args.spec_outfilename); for (ii=0;ii<nfiles;ii++){ delete d_tims[ii]; delete pspecs[ii]; } return 0; }
void HehFeatureGen::Compute(const ImageRGB<byte>& imagergb, const ImageF& image, const MatI& seg) { // General notes: // We must always be careful to deal with normalized image // coordinates, which range from 0 to 1 in both dimensions. This // includes pixel locations, vanishing point locations, and line // intersections. // Any homogeneous vector can represent a point at infinity, which // will manifest as an INF if we Project() it. This is fine so // long as we keep track of which coordinates might potentially be // INF and only do sensible things with them -- e.g. no sqrt(), // atan2(), division between two variables that can both be INF, // etc. const int w = image.GetWidth(); const int h = image.GetHeight(); const int image_size = w*h; // Run the filter bank // TODO: implement LM filters. Here we just use 12 Gabor filters. vector<shared_ptr<ImageF> > responses; FilterBank filterbank(1); // *** should be 3 scales MakeGaborFilters(4, &filterbank.filters); filterbank.Run(image, &responses); // // Find lines and compute vanishing points // vpts.Compute(image); const int num_lines = vpts.lines.segments.size(); const int num_vpts = vpts.vanpts.size(); // // Pre-process the line segments // // Whether each pair of lines is nearly parallel MatI is_nearly_parallel(num_lines, num_lines); // Whether the intersection of each pair is right of center MatI isct_is_right(num_lines, num_lines); // Whether the intersection of each pair is below center MatI isct_is_bottom(num_lines, num_lines); // Histogram bin index for intersection of each pair, or -1 if outside MatI isct_bin(num_lines, num_lines); // Compute line intersections for (int i = 0; i < num_lines; i++) { const LineSegment& segi = *vpts.lines.segments[i]; is_nearly_parallel[i][i] = 0; for (int j = 1; j < num_lines; j++) { const LineSegment& segj = *vpts.lines.segments[j]; // Parallel ? float dtheta = segi.theta - segj.theta; dtheta = min(dtheta, M_PI-dtheta); is_nearly_parallel[i][j] = (dtheta <= kParallelThresh ? 1 : 0); if (is_nearly_parallel[i][j]) { // Intersection position relative to center Vec3 isct = Cross3D(segi.line, segj.line); isct[0] /= w; // can be +/- INF isct[1] /= h; // can be +/- INF const float eucl_x = Project(isct)[0]; const float eucl_y = Project(isct)[1]; isct_is_right[i][j] = eucl_x > 0.5; isct_is_bottom[i][j] = eucl_y > 0.5; // Compute distance from centre // don't take sqrt(radius_sqr) because radius_sqr can be INF const float radius_sqr = eucl_x*eucl_x + eucl_y*eucl_y; int rad_bin = -1; if (radius_sqr > kFarRadius*kFarRadius) { rad_bin = 1; } else if (radius_sqr > kNearRadius*kNearRadius) { rad_bin = 0; } // Compute orientation from centre if (rad_bin == -1) { isct_bin[i][j] = -1; } else { // Must do atan2 with homogeneous coords. It will stay sane in // the case of points-at-infinity float phi = atan2(isct[1] - 0.5*isct[2], isct[0] - 0.5*isct[2]); int phi_bin = static_cast<int>(phi*M_PI/kNumOrientBins)%kNumOrientBins; int bin = rad_bin * kNumOrientBins + phi_bin; isct_bin[i][j] = bin; } } is_nearly_parallel[j][i] = is_nearly_parallel[i][j]; isct_is_right[j][i] = isct_is_right[i][j]; isct_is_bottom[j][i] = isct_is_bottom[i][j]; isct_bin[j][i] = isct_bin[i][j]; } } // // Pre-process vanishing points // enum VanPointCla { kVptNone, kVptHoriz, kVptVert }; const float h_vpt_min = 0.5-kHorizVptThresh; const float h_vpt_max = 0.5+kHorizVptThresh; const float v_vpt_low = 0.5-kVertVptThresh; const float v_vpt_high = 0.5+kVertVptThresh; int num_hvpts = 0; float sum_hvpts_y = 0; vector<VectorFixed<2,float> > norm_vpts; // can contain INF VecI vanpt_cla(vpts.vanpts.size(), kVptNone); COUNTED_FOREACH(int i, const VecD& vpt, vpts.vanpts) { const float eucl_x = Project(vpt)[0] / w; // can be +/-INF const float eucl_y = Project(vpt)[1] / h; // can be +/-INF // Classify vanishing point as horizontal, vertical, or none norm_vpts.push_back(MakeVector<2,float>(eucl_x, eucl_y)); if (eucl_y > h_vpt_min && eucl_y < h_vpt_max) { vanpt_cla[i] = kVptHoriz; sum_hvpts_y += eucl_y; num_hvpts++; } else if (eucl_y < v_vpt_low || eucl_y > v_vpt_high) { vanpt_cla[i] = kVptVert; } } // Assume horizon is horizontal with Y coord equal to average of the // "horizontal" vanishing points float horizon_y = 0.5; if (num_hvpts > 0) { horizon_y = sum_hvpts_y / num_hvpts; } // Print vanpt clas DREPORT(vanpt_cla); // Initialize features int num_segments = seg.MaxValue()+1; features.resize(num_segments); for (int i = 0; i < features.size(); i++) { features[i].seen = VecI(num_lines, 0); } // // Compute sums over the image // for (int r = 0; r < h; r++) { const PixelRGB<byte>* imrgbrow = imagergb[r]; const PixelF* imrow = image[r]; const int* segrow = seg[r]; const int* linerow = vpts.lines.segment_map[r]; for (int c = 0; c < w; c++) { HehFeature& ftr = features[segrow[c]]; // Shape ftr.xs.push_back(1.0*c/w); ftr.ys.push_back(1.0*r/h); // Color PixelHSV<byte> hsv; const PixelRGB<byte>& rgb = imrgbrow[c]; ImageConversions::RGB2HSV(rgb.r, rgb.g, rgb.b, hsv.h, hsv.s, hsv.v); ftr.r_mean += rgb.r; ftr.g_mean += rgb.g; ftr.b_mean += rgb.b; ftr.h_mean += hsv.h; ftr.s_mean += hsv.s; ftr.v_mean += hsv.v; const int hbin = static_cast<int>(kNumHueBins * hsv.h / 256); const int sbin = static_cast<int>(kNumSatBins * hsv.s / 256); ftr.hue_hist[hbin]++; ftr.sat_hist[sbin]++; // Texture ImageRef pyrpos(c, r); int maxi; float maxv = -INFINITY; for (int i = 0; i < responses.size(); i++) { if (i > 0 && responses[i]->GetWidth() < responses[i-1]->GetWidth()) { pyrpos /= 2; } const float v = (*responses[i])[pyrpos].y; ftr.filter_means[i] += v; if (v > maxv) { maxv = v; maxi = i; } } ftr.filter_max_hist[maxi]++; // Lines const int line = linerow[c]; if (line != -1) { if (!ftr.seen[line]) { ftr.seen[line] = 1; ftr.lines.push_back(line); } ftr.num_line_pix++; const int line_cla = vanpt_cla[vpts.owners[line]]; if (line_cla == kVptVert) { ftr.pct_vert_vpt++; ftr.pct_vert_vpt_pix++; } else if (line_cla == kVptHoriz) { ftr.pct_horiz_vpt_pix++; } } } } // // Normalize over each segment // for (int i = 0; i < features.size(); i++) { HehFeature& ftr = features[i]; float size = ftr.xs.size(); // Shape ftr.area = size/image_size; sort_all(ftr.xs); sort_all(ftr.ys); ftr.x_mean = accumulate_all(ftr.xs, 0.0) / size; ftr.x_10pct = ftr.xs[size/10]; ftr.x_90pct = ftr.xs[size*9/10]; ftr.y_mean = accumulate_all(ftr.ys, 0.0) / size; ftr.y_10pct = ftr.ys[size/10]; ftr.y_90pct = ftr.ys[size*9/10]; ftr.y_mean_wrt_horizon = ftr.y_mean - horizon_y; ftr.y_10pct_wrt_horizon = ftr.y_10pct - horizon_y; ftr.y_90pct_wrt_horizon = ftr.y_90pct - horizon_y; // Color ftr.r_mean /= size; ftr.g_mean /= size; ftr.b_mean /= size; ftr.h_mean /= size; ftr.s_mean /= size; ftr.v_mean /= size; ftr.hue_hist /= size; ftr.sat_hist /= size; // Texture ftr.filter_means /= size; ftr.filter_max_hist /= size; // Lines int num_nearly_parallel = 0; BOOST_FOREACH(int i, ftr.lines) { BOOST_FOREACH(int j, ftr.lines) { if (i == j) continue; if (is_nearly_parallel[i][j]) { num_nearly_parallel++; ftr.pct_parallel_lines++; if (isct_is_right[i][j]) { ftr.pct_isct_right++; } if (isct_is_bottom[i][j]) { ftr.pct_isct_bottom++; } if (isct_bin[i][j] != -1) { ftr.isct_hist[isct_bin[i][j]]++; } } } } const int num_line_pairs = ftr.lines.size() * (ftr.lines.size()-1); const float sqrt_area = sqrt(size); // don't use normalized area! ftr.pct_line_pix = ftr.num_line_pix / sqrt_area; if (num_line_pairs > 0) { ftr.pct_parallel_lines /= num_line_pairs; } if (num_nearly_parallel > 0) { ftr.isct_hist /= num_nearly_parallel; ftr.isct_hist_entropy = GetEntropy(ftr.isct_hist); ftr.pct_isct_right /= num_nearly_parallel; ftr.pct_isct_bottom /= num_nearly_parallel; } // Vanishing points bool has_horiz_vpt = false; bool has_vert_vpt = false; float horiz_vpt_x; float vert_vpt_y; for (int i = 0; i < ftr.lines.size(); i++) { const int owner_vpt = vpts.owners[ftr.lines[i]]; if (vanpt_cla[owner_vpt] == kVptHoriz) { horiz_vpt_x = Project(norm_vpts[owner_vpt])[0]; // can be +/- INF has_horiz_vpt = true; } if (vanpt_cla[owner_vpt] == kVptHoriz) { vert_vpt_y = Project(norm_vpts[owner_vpt])[1]; // can be +/- INF has_vert_vpt = true; } } ftr.pct_vert_vpt_pix /= sqrt_area; ftr.pct_horiz_vpt_pix /= sqrt_area; if (ftr.num_line_pix > 0) { ftr.pct_vert_vpt /= ftr.num_line_pix; } if (has_horiz_vpt) { ftr.x_mean_wrt_hvpt = ftr.x_mean - horiz_vpt_x; // can be +/- INF ftr.x_10pct_wrt_hvpt = ftr.x_10pct - horiz_vpt_x; // can be +/- INF ftr.x_90pct_wrt_hvpt = ftr.x_90pct - horiz_vpt_x; // can be +/- INF } else { ftr.x_mean_wrt_hvpt = 0; ftr.x_10pct_wrt_hvpt = 0; ftr.x_90pct_wrt_hvpt = 0; } if (has_vert_vpt) { ftr.y_mean_wrt_vert_vpt = ftr.y_mean - vert_vpt_y; } else { ftr.y_mean_wrt_vert_vpt = 0; } } }