Example #1
0
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;
		}
	}
}