UVES Pipeline Reference Manual  5.4.6
uves_orderpos_hough.c
1 /* *
2  * This file is part of the ESO UVES Pipeline *
3  * Copyright (C) 2004,2005 European Southern Observatory *
4  * *
5  * This library is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA *
18  * */
19 
20 /*
21  * $Author: amodigli $
22  * $Date: 2012-03-02 17:01:40 $
23  * $Revision: 1.25 $
24  * $Name: not supported by cvs2svn $
25  * $Log: not supported by cvs2svn $
26  * Revision 1.24 2011/12/08 14:04:33 amodigli
27  * Fix warnings with CPL6
28  *
29  * Revision 1.23 2010/09/24 09:32:05 amodigli
30  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
31  *
32  * Revision 1.21 2010/01/04 14:02:54 amodigli
33  * less verbose Hough transform computation
34  *
35  * Revision 1.20 2007/08/27 12:31:08 amodigli
36  * uves_msg_progress should go from 0 to ymax as ymax is actually the maximum y pixel value where the Hoght transform should be computed. i fixed this and added more check statements
37  *
38  * Revision 1.19 2007/08/21 13:08:26 jmlarsen
39  * Removed irplib_access module, largely deprecated by CPL-4
40  *
41  * Revision 1.18 2007/06/06 08:17:33 amodigli
42  * replace tab with 4 spaces
43  *
44  * Revision 1.17 2007/04/24 12:50:29 jmlarsen
45  * Replaced cpl_propertylist -> uves_propertylist which is much faster
46  *
47  * Revision 1.16 2007/04/20 14:44:47 jmlarsen
48  * Minor output message change
49  *
50  * Revision 1.15 2007/04/17 09:34:38 jmlarsen
51  * Parametrize the assumption about consecutive orders (for FLAMES support)
52  *
53  * Revision 1.14 2007/04/12 12:02:41 jmlarsen
54  * Added assertions for documentation purposes
55  *
56  * Revision 1.13 2007/04/10 07:08:30 jmlarsen
57  * Make sure that detected orders are always consecutive
58  *
59  * Revision 1.12 2006/11/15 15:02:14 jmlarsen
60  * Implemented const safe workarounds for CPL functions
61  *
62  * Revision 1.10 2006/11/15 14:04:08 jmlarsen
63  * Removed non-const version of parameterlist_get_first/last/next which is
64  * already in CPL, added const-safe wrapper, unwrapper and deallocator functions
65  *
66  * Revision 1.9 2006/11/06 15:19:41 jmlarsen
67  * Removed unused include directives
68  *
69  * Revision 1.8 2006/08/17 13:56:53 jmlarsen
70  * Reduced max line length
71  *
72  * Revision 1.7 2006/06/08 08:42:53 jmlarsen
73  * Added support for computing Hough transform on image subwindow
74  *
75  * Revision 1.6 2006/06/01 14:43:17 jmlarsen
76  * Added missing documentation
77  *
78  * Revision 1.5 2006/04/06 08:45:15 jmlarsen
79  * Changed number of significant digits when printing percentage
80  *
81  * Revision 1.4 2006/03/09 13:58:26 jmlarsen
82  * Minor optimization of Hough calculation
83  *
84  * Revision 1.3 2006/03/03 13:54:11 jmlarsen
85  * Changed syntax of check macro
86  *
87  * Revision 1.2 2006/02/15 13:19:15 jmlarsen
88  * Reduced source code max. line length
89  *
90  * Revision 1.1 2006/02/03 07:46:30 jmlarsen
91  * Moved recipe implementations to ./uves directory
92  *
93  * Revision 1.24 2005/12/19 16:17:55 jmlarsen
94  * Replaced bool -> int
95  *
96  */
97 #ifdef HAVE_CONFIG_H
98 # include <config.h>
99 #endif
100 
101 /*----------------------------------------------------------------------------*/
105 /*----------------------------------------------------------------------------*/
106 
107 /*-----------------------------------------------------------------------------
108  Includes
109  -----------------------------------------------------------------------------*/
110 
111 #include <uves_orderpos_hough.h>
112 
113 #include <uves_utils.h>
114 #include <uves_utils_wrappers.h>
115 #include <uves_error.h>
116 #include <uves_msg.h>
117 
118 #include <cpl.h>
119 
120 /*-----------------------------------------------------------------------------
121  Defines
122  -----------------------------------------------------------------------------*/
123 
124 /* Define macros that map from x-coordinate (integer) in Hough space to slope
125  in image space (double) and the inverse function */
126 #define SLOPE(hx) ( MINSLOPE + ( ((double)(hx)) / SLOPERES ) ) * (MAXSLOPE - MINSLOPE)
127 #define SLOPEINV(a) \
128  uves_round_double( SLOPERES * ( ((double)(a)) - MINSLOPE ) / (MAXSLOPE - MINSLOPE))
129 
130 /* Convert from pixel coordinate to intersept, and the other way */
131 #define INTERSEPT(hy) (minintersept + hy)
132 #define INTERSEPTINV(b) (b - minintersept)
133 
135 /*-----------------------------------------------------------------------------
136  Forward declarations
137  -----------------------------------------------------------------------------*/
138 
139 static cpl_table *detect_lines(cpl_image *htrans, int minintersept,
140  const cpl_image *inputimage,
141  int NORDERS, bool norders_is_guess, int SAMPLEWIDTH,
142  double PTHRES, double MINSLOPE, double MAXSLOPE, int SLOPERES,
143  bool consecutive);
144 static cpl_error_code delete_peak(cpl_image *htrans, int minintersept, int hxmax, int hymax,
145  int SPACING, int imagewidth, int SAMPLEWIDTH,
146  double MINSLOPE, double MAXSLOPE, int SLOPERES);
147 static int firsttrace(int nx, int SAMPLEWIDTH);
148 static int calculate_spacing(const cpl_image *, int x);
149 static double autocorr(const cpl_image *image, int x, int shift);
150 static cpl_error_code update_max(const cpl_image *htrans, /* Hough image */
151  int *xmax, /* peak location */
152  int *ymax,
153  int SPACING, /* inter-order seperation */
154  int imagewidth, /* width of input image */
155  int SAMPLEWIDTH, /* seperation of traces in input image */
156  double MINSLOPE,
157  double MAXSLOPE,
158  int SLOPERES);
159 
160 /*----------------------------------------------------------------------------*/
192 /*----------------------------------------------------------------------------*/
193 
194 cpl_table *uves_hough(const cpl_image *image, int ymin, int ymax, int NORDERS,
195  bool norders_is_guess,
196  int SAMPLEWIDTH, double PTHRES, double MINSLOPE, double MAXSLOPE,
197  int SLOPERES, bool consecutive,
198  cpl_image **htrans, cpl_image **htrans_original)
199 {
200 
201  cpl_table *ordertable = NULL; /* The result table */
202 
203  int nx = 0; /* Dimensions of input image */
204  int ny = 0;
205  int minintersept = 0; /* The intersepts represented by the Hough image are
206  all integer values */
207  int maxintersept = 0; /* in the interval ] minintersept ; maxintersept ] */
208  int firstcol;
209  const double *image_data = NULL; /* For efficiency */
210  double *htrans_data = NULL;
211 
212  *htrans = NULL; /* Hough transform image (peaks deleted), returned */
213  *htrans_original = NULL; /* Hough transform image, returned */
214 
215  /* Check input */
216  assure_nomsg( image != NULL, CPL_ERROR_NULL_INPUT);
217  assure( cpl_image_get_type(image) == CPL_TYPE_DOUBLE, CPL_ERROR_INVALID_TYPE,
218  "Input image has wrong type. Must be of type double");
219  assure( 0 <= MINSLOPE, CPL_ERROR_ILLEGAL_INPUT,
220  "minslope = %f must be non-negative", MINSLOPE);
221  assure( 0 <= MAXSLOPE, CPL_ERROR_ILLEGAL_INPUT,
222  "maxslope = %f must be non-negative", MAXSLOPE);
223  assure( MINSLOPE < MAXSLOPE, CPL_ERROR_INCOMPATIBLE_INPUT, "minslope = %f; maxslope = %f",
224  MINSLOPE, MAXSLOPE);
225  assure( 0 < SLOPERES, CPL_ERROR_ILLEGAL_INPUT,
226  "Hough image width = %d, must be positive", SLOPERES);
227 
228  /* For efficiency don't support bad pixels (possible to do
229  later if needed) */
230  assure (cpl_image_count_rejected(image) == 0,
231  CPL_ERROR_UNSUPPORTED_MODE, "Input image has %" CPL_SIZE_FORMAT " bad pixels",
232  cpl_image_count_rejected(image));
233 
234 
235  if (MAXSLOPE > 0.5)
236  {
237  uves_msg_warning("Max possible slope is %f, which is larger than 0.5. "
238  "Results might be unreliable", MAXSLOPE);
239  }
240 
241  nx = cpl_image_get_size_x(image);
242  ny = cpl_image_get_size_y(image);
243 
244  assure( 1 <= ymin && ymin <= ymax && ymax <= ny, CPL_ERROR_ILLEGAL_INPUT,
245  "Illegal y-range: %d - %d (image height is %d)", ymin, ymax, ny);
246 
247  /* Calculate min. and max. intersepts represented.
248  For simplicity, the Hough image is always full size,
249  even if not (ymin == 1 && ymax == ny) */
250  maxintersept = ny;
251  minintersept = uves_round_double(0 - nx*MAXSLOPE);
252 
253  /* Create the Hough image. Pixels are initialsed to zero */
254  check( *htrans = cpl_image_new(SLOPERES, /* Image width */
255  maxintersept - minintersept, /* Image height */
256  CPL_TYPE_DOUBLE),
257  "Could not create image");
258 
259  check_nomsg( image_data = cpl_image_get_data_double_const(image) );
260  check_nomsg( htrans_data = cpl_image_get_data_double(*htrans) );
261 
262  uves_msg("Calculating Hough transform");
263 
264  /* Locate the leftmost trace column */
265  check_nomsg(firstcol = firsttrace(nx, SAMPLEWIDTH));
266 
267  check_nomsg(UVES_TIME_START("The loop"));
268 
269  /* Loop through input image subwindow and calculate the Hough image */
270  {
271  int x, y;
272  for (y = ymin; y <= ymax; y += 1)
273  {
274  //if use_guess_tab =1 it may happen that ymax< ny
275  //uves_msg_progress should have first argoment which goes from 0 till the
276  //second argument value, in our case from 0 to ymax.
277  check_nomsg(uves_msg_debug("Calculating Hough transform %d %d",
278  y - 1, ymax));
279 
280  for (x = firstcol; x <= nx; x += SAMPLEWIDTH)
281  {
282  /* Transform image point at (x,y) to line in Hough space */
283  double pixelvalue;
284  int hx, hy;
285 
286  for (hx = 1; hx <= SLOPERES; hx++)
287  {
288  check_nomsg(hy = INTERSEPTINV(uves_round_double(y - x*SLOPE(hx))));
289  /* y = intersept + slope * x */
290 
291  /* Slow: check( pixelvalue =
292  cpl_image_get(image, x, y, &pis_rejected),
293  "Could not read pixel at (%d, %d) in input image", x, y); */
294  check_nomsg(pixelvalue = image_data[(x-1) + (y-1)*nx]);
295 
296  /* Add the pixelvalue to Hough image (hx, hy) */
297  /* Not supported for now: if (!pis_rejected) */
298 
299  /* Slow: check( current =
300  cpl_image_get(*htrans, hx, hy, &pis_rejected),
301  "Could not read pixel at (%d, %d) in Hough image", hx, hy);
302  check(
303  cpl_image_set(*htrans, hx, hy, current + pixelvalue),
304  "Could not update pixel at (%d, %d) in Hough image",
305  hx, hy); */
306 
307  check_nomsg(htrans_data[(hx-1) + (hy-1)*SLOPERES] += pixelvalue);
308  }
309  }
310  }
311  }
312 
313  UVES_TIME_END;
314 
315  check( *htrans_original = cpl_image_duplicate(*htrans), "Error copying hough image");
316 
317  /* Calculate order table from Hough image */
318  check( ordertable = detect_lines(*htrans,
319  minintersept,
320  image,
321  NORDERS,
322  norders_is_guess,
323  SAMPLEWIDTH,
324  PTHRES,
325  MINSLOPE,
326  MAXSLOPE,
327  SLOPERES,
328  consecutive),
329  "Could not detect lines in hough image");
330 
331  passure( cpl_table_get_ncol(ordertable) == 4, "%" CPL_SIZE_FORMAT "", cpl_table_get_ncol(ordertable));
332  passure( cpl_table_has_column(ordertable, "Slope"), " ");
333  passure( cpl_table_has_column(ordertable, "Intersept"), " ");
334  passure( cpl_table_has_column(ordertable, "Spacing"), " ");
335  passure( cpl_table_has_column(ordertable, "Order"), " ");
336 
337  cleanup:
338  if (cpl_error_get_code() != CPL_ERROR_NONE)
339  {
340  uves_free_image(htrans);
341  uves_free_image(htrans_original);
342  uves_free_table(&ordertable);
343  }
344  return ordertable;
345 }
346 
347 /*----------------------------------------------------------------------------*/
396 /*----------------------------------------------------------------------------*/
397 
398 static cpl_table *detect_lines(cpl_image *htrans, int minintersept,
399  const cpl_image *inputimage, int NORDERS,
400  bool norders_is_guess,
401  int SAMPLEWIDTH, double PTHRES, double MINSLOPE,
402  double MAXSLOPE, int SLOPERES,
403  bool consecutive)
404 {
405  cpl_table *results = NULL; /* The result order table */
406 
407  /* Local variables */
408  cpl_stats *stats = NULL; /* Used for finding peaks in Hough image */
409  uves_propertylist *pl = NULL; /* Used for sorting the order table */
410 
411  bool automatic = false; /* Flag indicating automatic mode */
412  int tablesize = 0;
413  double intensity = 0; /* Line intensity */
414  double prev_intensity = 0; /* Intensity of previously detected line */
415  int norders_detected = 0; /* Peaks detected so far */
416  int SPACING = 0; /* Interorder spacing */
417  double globmax = 0; /* Global maximum value */
418 
419  /* Check input */
420  passure( htrans != NULL, " ");
421  passure( inputimage != NULL, " ");
422  passure( NORDERS >= 0, "%d", NORDERS);
423  passure( SAMPLEWIDTH > 0, "%d", SAMPLEWIDTH);
424  passure( 0 <= PTHRES && PTHRES <= 1, "%f", PTHRES);
425  passure( SLOPERES > 0, "%d", SLOPERES);
426 
427  /* Do we know how many orders to detect?
428  If not, enter automatic mode */
429  if (NORDERS == 0)
430  {
431  uves_msg("Could not find information about predicted number of orders. "
432  "Entering automatic mode (threshold = %f)", PTHRES);
433  automatic = true;
434  }
435  else
436  {
437  uves_msg("Searching for %d (%s) order lines",
438  NORDERS, (norders_is_guess) ? "or less" : "exactly");
439  automatic = false;
440  }
441 
442  /* Allocate memory */
443  if (automatic)
444  {
445  /* The input image height is a (conservative) upper limit
446  on the number of echelle orders in the image */
447  tablesize = cpl_image_get_size_y(inputimage);
448  }
449  else
450  {
451  tablesize = NORDERS;
452  }
453 
454  /* Initialize order table */
455  check(( results = cpl_table_new(tablesize),
456  cpl_table_new_column(results, "Slope", CPL_TYPE_DOUBLE),
457  cpl_table_new_column(results, "Intersept", CPL_TYPE_DOUBLE),
458  cpl_table_new_column(results, "Spacing", CPL_TYPE_INT)),
459  "Could not initialize order table");
460 
461  /* Find maximum in Hough image */
462  check( stats = cpl_stats_new_from_image(htrans, CPL_STATS_MAX | CPL_STATS_MAXPOS),
463  "Could not get statistics on Hough image");
464 
465  /*
466  * Remember the first (i.e. global) maximum.
467  * In 1st iteration, prev_intensity == intensity
468  */
469  check( globmax = cpl_stats_get_max(stats),
470  "Could not locate first maximum in hough image" );
471 
472  prev_intensity = globmax;
473 
474  /* Repeat until the predicted number of orders is found,
475  * or (in automatic mode) until the line intensity is less than threshold
476  */
477  while ( (!automatic &&
478  (norders_detected < NORDERS &&
479  (!norders_is_guess || cpl_stats_get_max(stats) >= PTHRES*prev_intensity)))
480  || (automatic
481  && cpl_stats_get_max(stats) >= PTHRES*prev_intensity)
482  )
483  {
484  int xmax = 0;
485  int ymax = 0;
486  double slope = 0;
487  double isept = 0;
488 
489  norders_detected += 1;
490  check((intensity = cpl_stats_get_max(stats),
491  xmax = cpl_stats_get_max_x(stats),
492  ymax = cpl_stats_get_max_y(stats)),
493  "Could not locate maximum");
494 
495  /* Print (normalized) intensity of detection */
496  uves_msg_debug("%d. detection: intensity = %f",
497  norders_detected, intensity/globmax * 100);
498 
499  /* Warn if intensity suddenly dropped */
500  if (intensity < PTHRES * prev_intensity)
501  {
502  uves_msg_warning("Intensity of %d. line is only "
503  "%f %% of %d. line. Detecting too many orders?",
504  norders_detected, intensity / prev_intensity * 100,
505  norders_detected - 1);
506  }
507  prev_intensity = intensity;
508 
509  /* After detecting the first peak, estimate the approximate average order spacing */
510  if (norders_detected == 1)
511  {
512  if (!automatic)
513  {
514  SPACING = uves_round_double(
515  cpl_image_get_size_y(inputimage) / NORDERS );
516  }
517  else
518  { /* If the number of orders to detect is unknown,
519  derive the interorder spacing from the peak locations
520  in the Hough image */
521  check( SPACING = calculate_spacing(htrans, xmax),
522  "Could not estimate interorder spacing");
523  }
524 
525  uves_msg("Estimated order spacing is %d pixels", SPACING);
526  }
527 
528  /* Get a more precise peak location */
529  check( update_max(htrans,
530  &xmax,
531  &ymax,
532  SPACING,
533  cpl_image_get_size_x(inputimage),
534  SAMPLEWIDTH,
535  MINSLOPE,
536  MAXSLOPE,
537  SLOPERES), "Could not update peak position");
538 
539  check( delete_peak(htrans,
540  minintersept, xmax, ymax, SPACING,
541  cpl_image_get_size_x(inputimage),
542  SAMPLEWIDTH,
543  MINSLOPE, MAXSLOPE, SLOPERES),
544  "Could not delete peak in hough image");
545 
546  slope = SLOPE(xmax);
547  isept = minintersept + ymax;
548 
549  /* Make sure that the detection terminates if caller specified 'bad' parameters */
550  assure( norders_detected <= tablesize, CPL_ERROR_ILLEGAL_OUTPUT,
551  "%d orders detected. This is way too many. "
552  "Try to decrease NORDERS (from %d) or increase PTHRES (from %f)",
553  norders_detected, NORDERS, PTHRES);
554 
555  check(( cpl_table_set_double(results, "Slope" , norders_detected - 1, slope),
556  cpl_table_set_double(results, "Intersept" , norders_detected - 1, isept),
557  cpl_table_set_int (results, "Spacing" , norders_detected - 1, SPACING)),
558  "Could add order line to order table");
559 
560  /* Locate the next potential line */
561  check(( uves_free_stats(&stats),
562  stats = cpl_stats_new_from_image(htrans, CPL_STATS_MAX | CPL_STATS_MAXPOS)),
563  "Could not get statistics on hough image");
564  }
565 
566  uves_msg("The intensity of the faintest line is %f of "
567  "the intensity of the brightest line", intensity / globmax);
568  uves_msg("Intensity of next (undetected) line is %f of the "
569  "intensity of the brightest line", cpl_stats_get_max(stats)/globmax);
570 
571  if ( cpl_stats_get_max(stats) > 0.5 * intensity )
572  {
573  uves_msg_warning("Brightest undetected line with intensity %.2f %% "
574  "of faintest line. Detecting too few orders?",
575  cpl_stats_get_max(stats) / intensity * 100);
576  }
577 
578  /* Clean up table */
579  check( cpl_table_set_size(results, norders_detected),
580  "Could not remove extra rows from order table");
581 
582  /* Sort the order table so that order numbers increase from
583  bottom (low y) to top (high y) of image */
584  check( uves_sort_table_1(results, "Intersept", false), /* reverse flag = false */
585  "Could not sort order table");
586 
587  /* Number orders, starting from 1 */
588  {
589  int i;
590  cpl_table_new_column(results, "Order", CPL_TYPE_INT);
591  for (i = 0; i < cpl_table_get_nrow(results); i++)
592  {
593  cpl_table_set_int(results, "Order", i, i+1);
594  }
595  }
596 
597  if (consecutive)
598  /* Make sure we have consecutive orders.
599  This assumes that the order separation varies with less than
600  50 % from one order to the next */
601  {
602  int i;
603  double dist = 0;
604  int minorder, maxorder;
605  int n_removed;
606 
607  /* From middle and up */
608  maxorder = -1;
609  for (i = cpl_table_get_nrow(results)/2;
610  i <= cpl_table_get_nrow(results) - 2 && maxorder < 0;
611  i++)
612  {
613  if (i == cpl_table_get_nrow(results)/2)
614  /* initialize dist */
615  {
616  dist =
617  cpl_table_get_double(results, "Intersept", i+1, NULL)-
618  cpl_table_get_double(results, "Intersept", i, NULL);
619  }
620  else
621  {
622  double new_dist =
623  cpl_table_get_double(results, "Intersept", i+1, NULL)-
624  cpl_table_get_double(results, "Intersept", i, NULL);
625 
626  uves_msg_debug("Order %d - %d separation = %.4f pixels",
627  cpl_table_get_int(results, "Order", i, NULL),
628  cpl_table_get_int(results, "Order", i+1, NULL),
629  new_dist);
630 
631  if (0.5*dist <= new_dist && new_dist <= 1.5*dist)
632  {
633  /* OK. It's probably the next consecutive order */
634  }
635  else
636  {
637  /* Something's wrong. Stop */
638  maxorder = cpl_table_get_int(results, "Order", i, NULL);
639 
640  uves_msg_warning("Order separation jumps from %.2f pixels to "
641  "%.2f pixels. Discarding order(s) %d and above",
642  dist, new_dist,
643  maxorder+1);
644  }
645  }
646  }
647 
648  /* From middle and down */
649  minorder = -1;
650  for (i = cpl_table_get_nrow(results)/2;
651  i >= 1 && minorder < 0;
652  i--)
653  {
654  if (i == cpl_table_get_nrow(results)/2)
655  /* initialize dist */
656  {
657  dist =
658  cpl_table_get_double(results, "Intersept", i, NULL)-
659  cpl_table_get_double(results, "Intersept", i-1, NULL);
660  }
661  else
662  {
663  double new_dist =
664  cpl_table_get_double(results, "Intersept", i, NULL)-
665  cpl_table_get_double(results, "Intersept", i-1, NULL);
666 
667  uves_msg_debug("Order %d - %d separation = %.4f pixels",
668  cpl_table_get_int(results, "Order", i-1, NULL),
669  cpl_table_get_int(results, "Order", i, NULL),
670  new_dist);
671 
672  if (0.5*dist <= new_dist && new_dist <= 1.5*dist)
673  {
674  /* OK. It's probably the next consecutive order */
675  }
676  else
677  {
678  /* Something's wrong. Stop */
679  minorder = cpl_table_get_int(results, "Order", i, NULL);
680 
681  uves_msg_warning("Order separation jumps from %.2f pixels to "
682  "%.2f pixels. Discarding order(s) %d and below",
683  dist, new_dist,
684  minorder-1);
685  }
686  }
687  }
688 
689  n_removed = 0;
690  if (maxorder > 0)
691  {
692  check_nomsg( n_removed += uves_erase_table_rows(results, "Order",
693  CPL_GREATER_THAN, maxorder));
694  }
695  if (minorder > 0)
696  {
697  check_nomsg( n_removed += uves_erase_table_rows(results, "Order",
698  CPL_LESS_THAN, minorder));
699  }
700 
701  uves_msg_debug("%d order(s) removed", n_removed);
702  norders_detected -= n_removed;
703  }
704 
705  /* Renumber orders, starting from 1 */
706  {
707  int i;
708  for (i = 0; i < cpl_table_get_nrow(results); i++)
709  {
710  cpl_table_set_int(results, "Order", i, i+1);
711  }
712  }
713 
714  uves_msg("Hough transform detected %d orders", norders_detected);
715 
716  passure( norders_detected == cpl_table_get_nrow(results), "%d %" CPL_SIZE_FORMAT "",
717  norders_detected, cpl_table_get_nrow(results));
718 
719  cleanup:
720  uves_free_stats(&stats);
721  uves_free_propertylist(&pl);
722  if (cpl_error_get_code() != CPL_ERROR_NONE)
723  {
724  uves_free_table(&results);
725  }
726 
727  return results;
728 }
729 
730 
731 /*----------------------------------------------------------------------------*/
755 /*----------------------------------------------------------------------------*/
756 static cpl_error_code update_max(const cpl_image *htrans,
757  int *xmax,
758  int *ymax,
759  int SPACING,
760  int imagewidth,
761  int SAMPLEWIDTH,
762  double MINSLOPE,
763  double MAXSLOPE,
764  int SLOPERES)
765 {
766  const int nx = cpl_image_get_size_x(htrans);
767  const int ny = cpl_image_get_size_y(htrans);
768  const int slope = -imagewidth/2; /* slope of line in hough space */
769  const double numberoftraces = 1 + imagewidth/SAMPLEWIDTH;
770  int pis_rejected;
771 
772  /* Only look at pixel values above this threshold */
773  double threshold = (1 - 0.5 / numberoftraces) *
774  cpl_image_get(htrans, *xmax, *ymax, &pis_rejected);
775  if (threshold < 0.99) threshold = 0.99;
776 
777  assure (cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
778  "Could not read Hough image data");
779 
780  {
781  double sum = 0;
782  double mx = 0;
783  double my = 0;
784  int hx, hy;
785  for (hx = 1; hx <= SLOPERES; hx++)
786  {
787  int rowcenter =
788  uves_round_double(
789  *ymax + slope*(hx - *xmax)/((double)SLOPERES)*(MAXSLOPE - MINSLOPE));
790 
791  /* It would be more correct to look at pixels from (rowcenter - LINEWIDTH/2)
792  to (rowcenter + LINEWIDTH/2) where LINEWIDTH is the echelle line width
793  (i.e. different from SPACING) But empirically it doesn't really make
794  a difference if we just use the pixel at rowcenter (for each x) */
795 
796  for (hy = rowcenter - 0*SPACING/2; hy <= rowcenter + 0*SPACING/2; hy++)
797  {
798  if (1 <= hx && hx <= nx &&
799  1 <= hy && hy <= ny)
800  {
801  double pixelvalue = cpl_image_get(
802  htrans, hx, hy, &pis_rejected);
803  if (!pis_rejected && pixelvalue >= threshold)
804  {
805  mx += hx*pixelvalue;
806  my += hy*pixelvalue;
807  sum += pixelvalue;
808  }
809  }
810  }
811 
812  }
813 
814  uves_msg_debug("Peak position in Hough space changed from (%d, %d)", *xmax, *ymax);
815  *xmax = uves_round_double(mx/sum);
816  *ymax = uves_round_double(my/sum);
817  uves_msg_debug("to (%d, %d)", *xmax, *ymax);
818  }
819 
820  cleanup:
821  return cpl_error_get_code();
822 }
823 
824 /*----------------------------------------------------------------------------*/
835 /*----------------------------------------------------------------------------*/
836 
837 static int calculate_spacing(const cpl_image *image, int x)
838 {
839  int shift = 0;
840  double autoc = autocorr(image, x, shift);
841  double previous;
842  assure (cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
843  "Could not calculate autocorrelation function");
844 
845  uves_msg_debug("Autocorrelation(shift=%d) = %f", shift, autoc);
846 
847  do{
848  previous = autoc;
849  shift += 1;
850  check( autoc = autocorr(image, x, shift),
851  "Could not calculate autocorrelation function");
852  uves_msg_debug("Autocorrelation(shift=%d) = %f", shift, autoc);
853  } while (autoc <= previous);
854 
855  cleanup:
856  return 2*(shift - 1);
857  /* First minimum of the autocorrelation function is half spacing */
858 }
859 
860 
861 /*----------------------------------------------------------------------------*/
874 /*----------------------------------------------------------------------------*/
875 static double autocorr(const cpl_image *image, const int x, const int shift)
876 {
877  double result = 0;
878  const int ny = cpl_image_get_size_y(image);
879  assure (cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
880  "Could not read image dimensions");
881 
882  if( shift >= ny ) return 0;
883 
884  {
885  double sum = 0;
886  int y;
887  int number_of_points = 0;
888  for (y = 1; y <= ny - shift; y++){
889  int pis_rejected;
890  double pixelvalue;
891 
892  pixelvalue = cpl_image_get(image, x, y, &pis_rejected) *
893  cpl_image_get(image, x, y + shift, &pis_rejected);
894 
895  if (!pis_rejected){
896  sum += pixelvalue;
897  number_of_points += 1;
898  }
899  }
900  assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
901  "Error reading image pixel values");
902 
903  if (number_of_points > 0)
904  {
905  result = sum / number_of_points;
906  }
907  else
908  {
909  result = 0;
910  }
911  }
912 
913  cleanup:
914  return result;
915 }
916 
917 /*----------------------------------------------------------------------------*/
929 /*----------------------------------------------------------------------------*/
930 static int firsttrace(int nx, int SAMPLEWIDTH)
931 {
932  int result = nx/2;
933 
934  while(result - SAMPLEWIDTH >= 1)
935  {
936  result -= SAMPLEWIDTH;
937  }
938 
939  return result;
940 }
941 
942 /*----------------------------------------------------------------------------*/
965 /*----------------------------------------------------------------------------*/
966 static cpl_error_code delete_peak(cpl_image *htrans, int minintersept,
967  int hxmax, int hymax,
968  int SPACING, int imagewidth, int SAMPLEWIDTH,
969  double MINSLOPE, double MAXSLOPE, int SLOPERES)
970 {
971  const int ny = cpl_image_get_size_y(htrans);
972  int tracecol;
973  int firstcol = firsttrace(imagewidth, SAMPLEWIDTH);
974 
975  assure (cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
976  "Could not read hough image data");
977 
978  /* We reverse the calculation of the Hough transform */
979  for (tracecol = firstcol; tracecol <= imagewidth; tracecol += SAMPLEWIDTH){
980  /* Get y-coordinate in raw image */
981  double slope = SLOPE(hxmax);
982  double intersept = minintersept + hymax;
983  double imagey = intersept + slope*tracecol;
984 
985  /* Now erase all points in the Hough image that were caused
986  by the point (tracecol, imagey) in the input image */
987  int hx, hy;
988  for (hx = 1; hx <= SLOPERES; hx++){
989  slope = SLOPE(hx);
990  intersept = imagey - slope*tracecol;
991  for (hy = (intersept - minintersept) - SPACING/3;
992  hy <= (intersept - minintersept) + SPACING/3;
993  hy++) {
994  if (0 < hy && hy <= ny) {
995  check( cpl_image_set(htrans, hx, hy, 0),
996  "Could not write pixel at (%d, %d)", hx, hy);
997  }
998  }
999  }
1000  }
1001 
1002  cleanup:
1003  return cpl_error_get_code();
1004 }
1005 
1006 /*----------------------------------------------------------------------------*/
1017 /*----------------------------------------------------------------------------*/
1018 cpl_error_code uves_draw_orders(const cpl_table *ordertable, cpl_image *image)
1019 {
1020  double penvalue;
1021  int nx;
1022  int ny;
1023  cpl_stats *stats = NULL;
1024  int nrows;
1025  int i;
1026 
1027  /* Check input */
1028  passure( image != NULL, " ");
1029  passure( ordertable != NULL, " ");
1030  passure( cpl_table_has_column(ordertable, "Intersept"), " ");
1031  passure( cpl_table_has_column(ordertable, "Slope" ), " ");
1032 
1033  nx = cpl_image_get_size_x(image);
1034  ny = cpl_image_get_size_y(image);
1035 
1036  /* Calculate pen value */
1037  check( stats = cpl_stats_new_from_image(image, CPL_STATS_MAX),
1038  "Could not get statistics on input image");
1039  check( penvalue = 2*cpl_stats_get_max(stats),
1040  "Could not find image maximum value" );
1041 
1042  /* Draw lines in ordertable on image */
1043  check ( nrows = cpl_table_get_nrow(ordertable),
1044  "Could not read number of rows in ordertable");
1045  for (i = 0; i < nrows; i++)
1046  {
1047  int x;
1048  double intersept, slope;
1049 
1050  check(( intersept = cpl_table_get_double(ordertable, "Intersept", i, NULL),
1051  slope = cpl_table_get_double(ordertable, "Slope", i, NULL)),
1052  "Could not read 'Intersept' and 'Slope' from ordertable");
1053 
1054  for (x = 1; x <= nx; x++){
1055  double yd = intersept + x * slope;
1056  int y = uves_round_double(yd);
1057 
1058  if (0 < y && y <= ny)
1059  {
1060  cpl_image_set(image, x, y, penvalue);
1061  }
1062  }
1063  assure( cpl_error_get_code() == CPL_ERROR_NONE, cpl_error_get_code(),
1064  "Could not draw order in table row #%d", i);
1065  }
1066 
1067  cleanup:
1068  uves_free_stats(&stats);
1069  return cpl_error_get_code();
1070 }
#define uves_msg_warning(...)
Print an warning message.
Definition: uves_msg.h:87
#define check_nomsg(CMD)
Definition: uves_error.h:204
static cpl_table * detect_lines(cpl_image *htrans, int minintersept, const cpl_image *inputimage, int NORDERS, bool norders_is_guess, int SAMPLEWIDTH, double PTHRES, double MINSLOPE, double MAXSLOPE, int SLOPERES, bool consecutive)
Detect order lines from the Hough image.
cpl_table * uves_hough(const cpl_image *image, int ymin, int ymax, int NORDERS, bool norders_is_guess, int SAMPLEWIDTH, double PTHRES, double MINSLOPE, double MAXSLOPE, int SLOPERES, bool consecutive, cpl_image **htrans, cpl_image **htrans_original)
Compute Hough transform and detect lines.
#define passure(BOOL,...)
Definition: uves_error.h:207
#define uves_msg(...)
Print a message on 'info' or 'debug' level.
Definition: uves_msg.h:119
static cpl_error_code update_max(const cpl_image *htrans, int *xmax, int *ymax, int SPACING, int imagewidth, int SAMPLEWIDTH, double MINSLOPE, double MAXSLOPE, int SLOPERES)
Improve position of a peak in the Hough image.
static double autocorr(const cpl_image *image, int x, int shift)
Calculate auto-correlation function of image column.
#define uves_msg_debug(...)
Print a debug message.
Definition: uves_msg.h:97
cpl_error_code uves_draw_orders(const cpl_table *ordertable, cpl_image *image)
Draw detected order lines.
#define assure_nomsg(BOOL, CODE)
Definition: uves_error.h:177
static int firsttrace(int nx, int SAMPLEWIDTH)
Get the first trace column.
static cpl_error_code delete_peak(cpl_image *htrans, int minintersept, int hxmax, int hymax, int SPACING, int imagewidth, int SAMPLEWIDTH, double MINSLOPE, double MAXSLOPE, int SLOPERES)
Delete peak in Hough image.
static int calculate_spacing(const cpl_image *, int x)
Estimate spacing of order lines.
#define check(CMD,...)
Definition: uves_error.h:198