def compute_fw_at_frac_max_1d_simple(Y, xc, X=None, f=0.5): """Compute the full width at fraction f of the maximum""" yy = np.asarray(Y) if yy.ndim != 1: raise ValueError('array must be 1-d') if yy.size == 0: raise ValueError('array is empty') if X is None: xx = np.arange(yy.shape[0]) else: xx = X xpix = coor_to_pix_1d(xc - xx[0]) try: peak = yy[xpix] except IndexError: raise ValueError('peak is out of array') fwhm_x, _codex, _msgx = compute_fwhm_1d(xx, yy - f * peak, xc, xpix) return peak, fwhm_x
def _locate_bar_gen(icut, epos, transform1, transform2): """Generic function for the fine position of the CSU""" epos_pix = coor_to_pix_1d(epos) # transform -> epos_pix_s = transform1(epos_pix) icut2 = transform2(icut) # try: res = position_half_h(icut2, epos_pix_s) xint_s, next_peak_s, wpos1_s, wpos2_s, background_level, half_height = res # xint = transform1(xint_s) # epos_f = xint error = 0 except ValueError: error = 2 epos_f = epos return epos_pix, epos_f, error
def compute_fwhm_2d_simple(img, xc, yc): X = np.arange(0, img.shape[1], 1.0) Y = np.arange(0, img.shape[0], 1.0) xpix = coor_to_pix_1d(xc - X[0]) ypix = coor_to_pix_1d(yc - Y[0]) peak = img[ypix, xpix] res11 = img[ypix, :] res22 = img[:, xpix] fwhm_x, _codex, _msgx = compute_fwhm_1d(X, res11 - 0.5 * peak, xc, xpix) fwhm_y, _codey, _msgy = compute_fwhm_1d(Y, res22 - 0.5 * peak, yc, ypix) return peak, fwhm_x, fwhm_y
def compute_fwhm_2d_spline(img, xc, yc): Y = np.arange(0.0, img.shape[1], 1.0) X = np.arange(0.0, img.shape[0], 1.0) xpix = coor_to_pix_1d(xc) ypix = coor_to_pix_1d(yc) # The image is already cropped bb = itpl.RectBivariateSpline(X, Y, img) # We assume that the peak is in the center... peak = bb(xc, yc)[0, 0] U = X V = bb.ev(U, [yc for _ in U]) - 0.5 * peak fwhm_x, _codex, _msgx = compute_fwhm_1d(U, V, yc, ypix) U = Y V = bb.ev([xc for _ in U], U) - 0.5 * peak fwhm_y, _codey, _msgy = compute_fwhm_1d(U, V, xc, xpix) return peak, fwhm_x, fwhm_y
def compute_slits(self, hdulist, csu_conf): self.logger.debug('finding borders of slits') self.logger.debug('not strictly necessary...') data = hdulist[0].data self.logger.debug('dtype of data %s', data.dtype) self.logger.debug('median filter (3x3)') image_base = ndi.filters.median_filter(data, size=3) # Cast as original type for skimage self.logger.debug('casting image to unit16 (for skimage)') iuint16 = np.iinfo(np.uint16) image = np.clip(image_base, iuint16.min, iuint16.max).astype(np.uint16) self.logger.debug('compute Sobel filter') # FIXME: compute sob and sob_v is redundant sob = filt.sobel(image) self.save_intermediate_array(sob, 'sobel_image.fits') sob_v = filt.sobel_v(image) self.save_intermediate_array(sob_v, 'sobel_v_image.fits') # Compute detector coordinates of bars all_coords_virt = np.empty((110, 2)) all_coords_real = np.empty((110, 2)) # Origin of coordinates is 1 for bar in csu_conf.bars.values(): all_coords_virt[bar.idx - 1] = bar.xpos, bar.y0 # Origin of coordinates is 1 for this function _x, _y = dist.exvp(all_coords_virt[:, 0], all_coords_virt[:, 1]) all_coords_real[:, 0] = _x all_coords_real[:, 1] = _y # FIXME: hardcoded value h = 16 slit_h_virt = 16.242 slit_h_tol = 3 slits_bb = {} mask1 = np.zeros_like(hdulist[0].data) for idx in range(EMIR_NBARS): lbarid = idx + 1 rbarid = lbarid + EMIR_NBARS ref_x_l_v, ref_y_l_v = all_coords_virt[lbarid - 1] ref_x_r_v, ref_y_r_v = all_coords_virt[rbarid - 1] ref_x_l_d, ref_y_l_d = all_coords_real[lbarid - 1] ref_x_r_d, ref_y_r_d = all_coords_real[rbarid - 1] width_v = ref_x_r_v - ref_x_l_v # width_d = ref_x_r_d - ref_x_l_d if (ref_y_l_d >= 2047 + h) or (ref_y_l_d <= 1 - h): # print('reference y position is outlimits, skipping') continue if width_v < 5: # print('width is less than 5 pixels, skipping') continue plot = False regionw = 12 px1 = coor_to_pix_1d(ref_x_l_d) - 1 px2 = coor_to_pix_1d(ref_x_r_d) - 1 prow = coor_to_pix_1d(ref_y_l_d) - 1 comp_l, comp_r = calc0(image, sob_v, prow, px1, px2, regionw, h=h, plot=plot, lbarid=lbarid, rbarid=rbarid, plot2=False) if np.any(np.isnan([comp_l, comp_r])): self.logger.warning("converting NaN value, border of=%d", idx + 1) self.logger.warning("skipping bar=%d", idx + 1) continue elif comp_l > comp_r: # Not refining self.logger.warning("computed left border of=%d greater than right border", idx + 1) comp2_l, comp2_r = px1, px2 else: region2 = 5 px21 = coor_to_pix_1d(comp_l) px22 = coor_to_pix_1d(comp_r) comp2_l, comp2_r = calc0(image, sob_v, prow, px21, px22, region2, refine=True, plot=plot, lbarid=lbarid, rbarid=rbarid, plot2=False) if np.any(np.isnan([comp2_l, comp2_r])): self.logger.warning("converting NaN value, border of=%d", idx + 1) comp2_l, comp2_r = comp_l, comp_r elif comp2_l > comp2_r: # Not refining self.logger.warning("computed left border of=%d greater than right border", idx + 1) comp2_l, comp2_r = comp_l, comp_r # print('slit', lbarid, '-', rbarid, comp_l, comp_r) # print('pos1', comp_l, comp_r) # print('pos2', comp2_l, comp2_r) xpos1_virt, _ = dist.pvex(comp2_l + 1, ref_y_l_d) xpos2_virt, _ = dist.pvex(comp2_r + 1, ref_y_r_d) y1_virt = ref_y_l_v - slit_h_virt - slit_h_tol y2_virt = ref_y_r_v + slit_h_virt + slit_h_tol _, y1 = dist.exvp(xpos1_virt + 1, y1_virt) _, y2 = dist.exvp(xpos2_virt + 1, y2_virt) # print(comp2_l, comp2_r, y1 - 1, y2 - 1) cbb = BoundingBox.from_coordinates(comp2_l, comp2_r, y1 - 1, y2 - 1) slits_bb[lbarid] = cbb mask1[cbb.slice] = lbarid self.save_intermediate_array(mask1, 'mask_slit_computed.fits') return slits_bb
def run(self, rinput): self.logger.info('starting processing for bars detection') flow = self.init_filters(rinput) hdulist = basic_processing_with_combination(rinput, flow=flow) hdr = hdulist[0].header self.set_base_headers(hdr) try: rotang = hdr['ROTANG'] tsutc1 = hdr['TSUTC1'] dtub, dtur = datamodel.get_dtur_from_header(hdr) csupos = datamodel.get_csup_from_header(hdr) csusens = datamodel.get_cs_from_header(hdr) except KeyError as error: self.logger.error(error) raise numina.exceptions.RecipeError(error) self.logger.debug('finding bars') # Processed array arr = hdulist[0].data # Median filter of processed array (two times) mfilter_size = rinput.median_filter_size self.logger.debug('median filtering X, %d columns', mfilter_size) arr_median = median_filter(arr, size=(1, mfilter_size)) self.logger.debug('median filtering X, %d rows', mfilter_size) arr_median = median_filter(arr_median, size=(mfilter_size, 1)) # Median filter of processed array (two times) in the other direction # for Y coordinates self.logger.debug('median filtering Y, %d rows', mfilter_size) arr_median_alt = median_filter(arr, size=(mfilter_size, 1)) self.logger.debug('median filtering Y, %d columns', mfilter_size) arr_median_alt = median_filter(arr_median_alt, size=(1, mfilter_size)) xfac = dtur[0] / EMIR_PIXSCALE yfac = -dtur[1] / EMIR_PIXSCALE vec = [yfac, xfac] self.logger.debug('DTU shift is %s', vec) # and the table of approx positions of the slits barstab = rinput.bars_nominal_positions # Currently, we only use fields 0 and 2 # of the nominal positions file # Number or rows used # These other parameters cab be tuned also bstart = 1 bend = 2047 self.logger.debug('ignoring columns outside %d - %d',bstart, bend-1) # extract a region to average wy = (rinput.average_box_row_size // 2) wx = (rinput.average_box_col_size // 2) self.logger.debug('extraction window is %d rows, %d cols',2*wy+1, 2*wx+1) # Fit the peak with these points wfit = 2 * (rinput.fit_peak_npoints // 2) + 1 self.logger.debug('fit with %d points', wfit) # Minimum threshold threshold = 5 * EMIR_RON # Savitsky and Golay (1964) filter to compute the X derivative # scipy >= xx has a savgol_filter function # for compatibility we do it manually allpos = {} ypos3_kernel = None slits = numpy.zeros((EMIR_NBARS, 8), dtype='float') self.logger.info('start finding bars') for ks in [3, 5, 7, 9]: self.logger.debug('kernel size is %d', ks) # S and G kernel for derivative kw = ks * (ks*ks-1) / 12.0 coeffs_are = -numpy.arange((1-ks)//2, (ks-1)//2 + 1) / kw if ks == 3: ypos3_kernel = coeffs_are self.logger.debug('kernel weights are %s', coeffs_are) self.logger.debug('derive image in X direction') arr_deriv = convolve1d(arr_median, coeffs_are, axis=-1) # Axis 0 is # self.logger.debug('derive image in Y direction (with kernel=3)') arr_deriv_alt = convolve1d(arr_median_alt, ypos3_kernel, axis=0) positions = [] for coords in barstab: lbarid = int(coords[0]) rbarid = lbarid + EMIR_NBARS ref_y_coor = coords[1] + vec[1] poly_coeffs = coords[2:] prow = coor_to_pix_1d(ref_y_coor) - 1 fits_row = prow + 1 # FITS pixel index # A function that returns the center of the bar # given its X position def center_of_bar(x): # Pixel values are 0-based return polyval(x+1-vec[0], poly_coeffs) + vec[1] - 1 self.logger.debug('looking for bars with ids %d - %d', lbarid, rbarid) self.logger.debug('reference y position is Y %7.2f', ref_y_coor) # if ref_y_coor is outlimits, skip this bar # ref_y_coor is in FITS format if (ref_y_coor >= 2047) or (ref_y_coor <= 1): self.logger.debug('reference y position is outlimits, skipping') positions.append([lbarid, fits_row, fits_row, 1, 0, 3]) positions.append([rbarid, fits_row, fits_row, 1, 0, 3]) continue # Left bar self.logger.debug('measure left border (%d)', lbarid) centery, xpos, fwhm, st = char_bar_peak_l(arr_deriv, prow, bstart, bend, threshold, center_of_bar, wx=wx, wy=wy, wfit=wfit) xpos1 = xpos positions.append([lbarid, centery+1, fits_row, xpos+1, fwhm, st]) # Right bar self.logger.debug('measure rigth border (%d)', rbarid) centery, xpos, fwhm, st = char_bar_peak_r(arr_deriv, prow, bstart, bend, threshold, center_of_bar, wx=wx, wy=wy, wfit=wfit) positions.append([rbarid, centery+1, fits_row, xpos+1, fwhm, st]) xpos2 = xpos # if st == 0: self.logger.debug('measure top-bottom borders') try: y1, y2, statusy = char_bar_height(arr_deriv_alt, xpos1, xpos2, centery, threshold, wh=35, wfit=wfit) except Exception as error: self.logger.warning('Error computing height: %s', error) statusy = 44 if statusy in [0, 40]: # Main border is detected positions[-1][1] = y2 + 1 positions[-2][1] = y2 + 1 else: # Update status positions[-1][-1] = 4 positions[-2][-1] = 4 else: self.logger.debug('slit is not complete') y1, y2 = 0, 0 # Update positions self.logger.debug('bar %d centroid-y %9.4f, row %d x-pos %9.4f, FWHM %6.3f, status %d', *positions[-2]) self.logger.debug('bar %d centroid-y %9.4f, row %d x-pos %9.4f, FWHM %6.3f, status %d', *positions[-1]) if ks == 5: slits[lbarid - 1] = [xpos1, y2, xpos2, y2, xpos2, y1, xpos1, y1] # FITS coordinates slits[lbarid - 1] += 1.0 self.logger.debug('inserting bars %d-%d into "slits"', lbarid, rbarid) allpos[ks] = numpy.asarray(positions, dtype='float') # GCS doesn't like lists of lists self.logger.debug('end finding bars') result = self.create_result(frame=hdulist, slits=slits, positions9=allpos[9], positions7=allpos[7], positions5=allpos[5], positions3=allpos[3], DTU=dtub, ROTANG=rotang, TSUTC1=tsutc1, csupos=csupos, csusens=csusens, ) return result
def run(self, rinput): logger = logging.getLogger('numina.recipes.emir') logger.info('starting processing for bars detection') flow = self.init_filters(rinput) hdulist = basic_processing_with_combination(rinput, flow=flow) hdr = hdulist[0].header self.set_base_headers(hdr) try: rotang = hdr['ROTANG'] dtub, dtur = datamodel.get_dtur_from_header(hdr) csupos = datamodel.get_csup_from_header(hdr) csusens = datamodel.get_cs_from_header(hdr) except KeyError as error: logger.error(error) raise numina.exceptions.RecipeError(error) logger.debug('finding bars') arr = hdulist[0].data # Median filter logger.debug('median filtering') mfilter_size = rinput.median_filter_size arr_median = median_filter(arr, size=mfilter_size) # Image is mapped between 0 and 1 # for the full range [0: 2**16] logger.debug('image scaling to 0-1') arr_grey = normalize_raw(arr_median) # Find borders logger.debug('find borders') canny_sigma = rinput.canny_sigma # These threshols corespond roughly with # value x (2**16 - 1) high_threshold = rinput.canny_high_threshold low_threshold = rinput.canny_low_threshold edges = canny(arr_grey, sigma=canny_sigma, high_threshold=high_threshold, low_threshold=low_threshold) # Number or rows used # These other parameters cab be tuned also total = 5 maxdist = 1.0 bstart = 100 bend = 1900 positions = [] nt = total // 2 xfac = dtur[0] / EMIR_PIXSCALE yfac = -dtur[1] / EMIR_PIXSCALE vec = [yfac, xfac] logger.debug('DTU shift is %s', vec) # Based on the 'edges image' # and the table of approx positions of the slits barstab = rinput.bars_nominal_positions # Currently, we only use fields 0 and 2 # of the nominal positions file for coords in barstab: lbarid = int(coords[0]) rbarid = lbarid + 55 ref_y_coor = coords[2] + vec[1] prow = coor_to_pix_1d(ref_y_coor) - 1 fits_row = prow + 1 # FITS pixel index logger.debug('looking for bars with ids %d - %d', lbarid, rbarid) logger.debug('reference y position is Y %7.2f', ref_y_coor) # Find the position of each bar bpos = find_position(edges, prow, bstart, bend, total) nbars_found = len(bpos) # If no bar is found, append and empty token if nbars_found == 0: logger.debug('bars %d, %d not found at row %d', lbarid, rbarid, fits_row) thisres1 = (lbarid, fits_row, 0, 0, 1) thisres2 = (rbarid, fits_row, 0, 0, 1) elif nbars_found == 2: # Order values by increasing X centl, centr = sorted(bpos, key=lambda cen: cen[0]) c1 = centl[0] c2 = centr[0] logger.debug('bars found at row %d between %7.2f - %7.2f', fits_row, c1, c2) # Compute FWHM of the collapsed profile cslit = arr_grey[prow - nt:prow + nt + 1, :] pslit = cslit.mean(axis=0) # Add 1 to return FITS coordinates epos, epos_f, error = locate_bar_l(pslit, c1) thisres1 = lbarid, fits_row, epos + 1, epos_f + 1, error epos, epos_f, error = locate_bar_r(pslit, c2) thisres2 = rbarid, fits_row, epos + 1, epos_f + 1, error elif nbars_found == 1: logger.warning( 'only 1 edge found at row %d, not yet implemented', fits_row) thisres1 = (lbarid, fits_row, 0, 0, 1) thisres2 = (rbarid, fits_row, 0, 0, 1) else: logger.warning( '3 or more edges found at row %d, not yet implemented', fits_row) thisres1 = (lbarid, fits_row, 0, 0, 1) thisres2 = (rbarid, fits_row, 0, 0, 1) positions.append(thisres1) positions.append(thisres2) logger.debug('end finding bars') result = self.create_result( frame=hdulist, positions=positions, DTU=dtub, ROTANG=rotang, csupos=csupos, csusens=csusens, param_median_filter_size=rinput.median_filter_size, param_canny_high_threshold=rinput.canny_high_threshold, param_canny_low_threshold=rinput.canny_low_threshold) return result
def run(self, rinput): logger = logging.getLogger('numina.recipes.emir') logger.info('starting processing for bars detection') flow = self.init_filters(rinput) hdulist = basic_processing_with_combination(rinput, flow=flow) hdr = hdulist[0].header self.set_base_headers(hdr) try: rotang = hdr['ROTANG'] dtub, dtur = datamodel.get_dtur_from_header(hdr) csupos = datamodel.get_csup_from_header(hdr) csusens = datamodel.get_cs_from_header(hdr) except KeyError as error: logger.error(error) raise numina.exceptions.RecipeError(error) logger.debug('finding bars') arr = hdulist[0].data # Median filter logger.debug('median filtering') mfilter_size = rinput.median_filter_size arr_median = median_filter(arr, size=mfilter_size) # Image is mapped between 0 and 1 # for the full range [0: 2**16] logger.debug('image scaling to 0-1') arr_grey = normalize_raw(arr_median) # Find borders logger.debug('find borders') canny_sigma = rinput.canny_sigma # These threshols corespond roughly with # value x (2**16 - 1) high_threshold = rinput.canny_high_threshold low_threshold = rinput.canny_low_threshold edges = canny(arr_grey, sigma=canny_sigma, high_threshold=high_threshold, low_threshold=low_threshold) # Number or rows used # These other parameters cab be tuned also total = 5 maxdist = 1.0 bstart = 100 bend = 1900 positions = [] nt = total // 2 xfac = dtur[0] / EMIR_PIXSCALE yfac = -dtur[1] / EMIR_PIXSCALE vec = [yfac, xfac] logger.debug('DTU shift is %s', vec) # Based on the 'edges image' # and the table of approx positions of the slits barstab = rinput.bars_nominal_positions # Currently, we only use fields 0 and 2 # of the nominal positions file for coords in barstab: lbarid = int(coords[0]) rbarid = lbarid + 55 ref_y_coor = coords[2] + vec[1] prow = coor_to_pix_1d(ref_y_coor) - 1 fits_row = prow + 1 # FITS pixel index logger.debug('looking for bars with ids %d - %d', lbarid, rbarid) logger.debug('reference y position is Y %7.2f', ref_y_coor) # Find the position of each bar bpos = find_position(edges, prow, bstart, bend, total) nbars_found = len(bpos) # If no bar is found, append and empty token if nbars_found == 0: logger.debug('bars %d, %d not found at row %d', lbarid, rbarid, fits_row) thisres1 = (lbarid, fits_row, 0, 0, 1) thisres2 = (rbarid, fits_row, 0, 0, 1) elif nbars_found == 2: # Order values by increasing X centl, centr = sorted(bpos, key=lambda cen: cen[0]) c1 = centl[0] c2 = centr[0] logger.debug('bars found at row %d between %7.2f - %7.2f', fits_row, c1, c2) # Compute FWHM of the collapsed profile cslit = arr_grey[prow-nt:prow+nt+1,:] pslit = cslit.mean(axis=0) # Add 1 to return FITS coordinates epos, epos_f, error = locate_bar_l(pslit, c1) thisres1 = lbarid, fits_row, epos + 1, epos_f + 1, error epos, epos_f, error = locate_bar_r(pslit, c2) thisres2 = rbarid, fits_row, epos + 1, epos_f + 1, error elif nbars_found == 1: logger.warning('only 1 edge found at row %d, not yet implemented', fits_row) thisres1 = (lbarid, fits_row, 0, 0, 1) thisres2 = (rbarid, fits_row, 0, 0, 1) else: logger.warning('3 or more edges found at row %d, not yet implemented', fits_row) thisres1 = (lbarid, fits_row, 0, 0, 1) thisres2 = (rbarid, fits_row, 0, 0, 1) positions.append(thisres1) positions.append(thisres2) logger.debug('end finding bars') result = self.create_result(frame=hdulist, positions=positions, DTU=dtub, ROTANG=rotang, csupos=csupos, csusens=csusens, param_median_filter_size=rinput.median_filter_size, param_canny_high_threshold=rinput.canny_high_threshold, param_canny_low_threshold=rinput.canny_low_threshold ) return result
def find_bars(hdulist, bars_nominal_positions, csupos, dtur, average_box_row_size=7, average_box_col_size=21, fit_peak_npoints=3, median_filter_size=5, logger=None): logger.debug('filtering image') # Processed array arr_median = median_filtering(hdulist, median_filter_size) xfac = dtur[0] / EMIR_PIXSCALE yfac = -dtur[1] / EMIR_PIXSCALE vec = [yfac, xfac] logger.debug('DTU shift is %s', vec) # and the table of approx positions of the slits barstab = bars_nominal_positions # Currently, we only use fields 0 and 2 # of the nominal positions file # Number or rows used # These other parameters cab be tuned also bstart = 1 bend = 2047 logger.debug('ignoring columns outside %d - %d', bstart, bend - 1) # extract a region to average # wy = (average_box_row_size // 2) # wx = (average_box_col_size // 2) # logger.debug('extraction window is %d rows, %d cols', 2 * wy + 1, 2 * wx + 1) # Fit the peak with these points # wfit = 2 * (fit_peak_npoints // 2) + 1 # logger.debug('fit with %d points', wfit) # Minimum threshold threshold = 5 * EMIR_RON # Savitsky and Golay (1964) filter to compute the X derivative # scipy >= xx has a savgol_filter function # for compatibility we do it manually allpos = {} slits = numpy.zeros((EMIR_NBARS, 8), dtype='float') logger.info('find peaks in derivative image') for ks in [3, 5, 7, 9]: logger.debug('kernel size is %d', ks) # S and G kernel for derivative kw = ks * (ks * ks - 1) / 12.0 coeffs_are = -numpy.arange((1 - ks) // 2, (ks - 1) // 2 + 1) / kw logger.debug('kernel weights are %s', coeffs_are) logger.debug('derive image in X direction') arr_deriv = convolve1d(arr_median, coeffs_are, axis=-1) # self.save_intermediate_array(arr_deriv, 'deriv_image_k%d.fits' % ks) # Axis 0 is # # logger.debug('derive image in Y direction (with kernel=3)') # arr_deriv_alt = convolve1d(arr_median_alt, ypos3_kernel, axis=0) positions = [] logger.info('using bar parameters') for idx in range(EMIR_NBARS): params_l = barstab[idx] params_r = barstab[idx + EMIR_NBARS] lbarid = int(params_l[0]) # CSUPOS for this bar rbarid = lbarid + EMIR_NBARS current_csupos_l = csupos[lbarid - 1] current_csupos_r = csupos[rbarid - 1] logger.debug('CSUPOS for bar %d is %f', lbarid, current_csupos_l) logger.debug('CSUPOS for bar %d is %f', rbarid, current_csupos_r) ref_y_coor_virt = params_l[1] # Do I need to add vec[1]? ref_x_l_coor_virt = params_l[3] + current_csupos_l * params_l[2] ref_x_r_coor_virt = params_r[3] + current_csupos_r * params_r[2] # Transform to REAL.. ref_x_l_coor, ref_y_l_coor = dist.exvp(ref_x_l_coor_virt, ref_y_coor_virt) ref_x_r_coor, ref_y_r_coor = dist.exvp(ref_x_r_coor_virt, ref_y_coor_virt) # FIXME: check if DTU has to be applied # ref_y_coor = ref_y_coor + vec[1] prow = coor_to_pix_1d(ref_y_l_coor) - 1 fits_row = prow + 1 # FITS pixel index logger.debug('looking for bars with ids %d - %d', lbarid, rbarid) logger.debug('ref Y virtual position is %7.2f', ref_y_coor_virt) logger.debug('ref X virtual positions are %7.2f %7.2f', ref_x_l_coor_virt, ref_x_r_coor_virt) logger.debug('ref X positions are %7.2f %7.2f', ref_x_l_coor, ref_x_r_coor) logger.debug('ref Y positions are %7.2f %7.2f', ref_y_l_coor, ref_y_r_coor) # if ref_y_coor is outlimits, skip this bar # ref_y_coor is in FITS format if (ref_y_l_coor >= 2047 + 16) or (ref_y_l_coor <= 1 - 16): logger.debug('reference y position is outlimits, skipping') positions.append( [lbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) positions.append( [rbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) continue # minimal width of the slit minwidth = 0.9 if abs(ref_x_l_coor_virt - ref_x_r_coor_virt) < minwidth: # FIXME: if width < minwidth fit peak in image logger.debug('slit is less than %d virt pixels, skipping', minwidth) positions.append( [lbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) positions.append( [rbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) continue # Left bar # Dont add +1 to virtual pixels logger.debug('measure left border (%d)', lbarid) regionw = 10 bstart1 = coor_to_pix_1d(ref_x_l_coor - regionw) bend1 = coor_to_pix_1d(ref_x_l_coor + regionw) + 1 centery, centery_virt, xpos1, xpos1_virt, fwhm, st = char_bar_peak_l( arr_deriv, prow, bstart1, bend1, threshold) insert1 = [ lbarid, centery + 1, centery_virt, fits_row, xpos1 + 1, xpos1_virt, fwhm, st ] positions.append(insert1) # Right bar # Dont add +1 to virtual pixels logger.debug('measure rigth border (%d)', rbarid) bstart2 = coor_to_pix_1d(ref_x_r_coor - regionw) bend2 = coor_to_pix_1d(ref_x_r_coor + regionw) + 1 centery, centery_virt, xpos2, xpos2_virt, fwhm, st = char_bar_peak_r( arr_deriv, prow, bstart2, bend2, threshold) # This centery/centery_virt should be equal to ref_y_coor_virt insert2 = [ rbarid, centery + 1, centery_virt, fits_row, xpos2 + 1, xpos2_virt, fwhm, st ] positions.append(insert2) # FIXME: hardcoded value y1_virt = ref_y_coor_virt - 16.242 y2_virt = ref_y_coor_virt + 16.242 _, y1 = dist.exvp(xpos1_virt + 1, y1_virt + 1) _, y2 = dist.exvp(xpos2_virt + 1, y2_virt + 1) # Update positions msg = 'bar %d, centroid-y %9.4f centroid-y virt %9.4f, ' \ 'row %d, x-pos %9.4f x-pos virt %9.4f, FWHM %6.3f, status %d' logger.debug(msg, *positions[-2]) logger.debug(msg, *positions[-1]) if ks == 5: slits[lbarid - 1] = numpy.array( [xpos1, y2, xpos2, y2, xpos2, y1, xpos1, y1]) # FITS coordinates slits[lbarid - 1] += 1.0 logger.debug('inserting bars %d-%d into "slits"', lbarid, rbarid) allpos[ks] = numpy.asarray( positions, dtype='float') # GCS doesn't like lists of lists return allpos, slits
def run(self, rinput): self.logger.info('starting processing for bars detection') flow = self.init_filters(rinput) hdulist = basic_processing_with_combination(rinput, flow=flow) hdr = hdulist[0].header self.set_base_headers(hdr) try: rotang = hdr['ROTANG'] tsutc1 = hdr['TSUTC1'] dtub, dtur = datamodel.get_dtur_from_header(hdr) csupos = datamodel.get_csup_from_header(hdr) csusens = datamodel.get_cs_from_header(hdr) except KeyError as error: self.logger.error(error) raise numina.exceptions.RecipeError(error) self.logger.debug('finding bars') # Processed array arr = hdulist[0].data # Median filter of processed array (two times) mfilter_size = rinput.median_filter_size self.logger.debug('median filtering X, %d columns', mfilter_size) arr_median = median_filter(arr, size=(1, mfilter_size)) self.logger.debug('median filtering X, %d rows', mfilter_size) arr_median = median_filter(arr_median, size=(mfilter_size, 1)) # Median filter of processed array (two times) in the other direction # for Y coordinates self.logger.debug('median filtering Y, %d rows', mfilter_size) arr_median_alt = median_filter(arr, size=(mfilter_size, 1)) self.logger.debug('median filtering Y, %d columns', mfilter_size) arr_median_alt = median_filter(arr_median_alt, size=(1, mfilter_size)) xfac = dtur[0] / EMIR_PIXSCALE yfac = -dtur[1] / EMIR_PIXSCALE vec = [yfac, xfac] self.logger.debug('DTU shift is %s', vec) # and the table of approx positions of the slits barstab = rinput.bars_nominal_positions # Currently, we only use fields 0 and 2 # of the nominal positions file # Number or rows used # These other parameters cab be tuned also bstart = 1 bend = 2047 self.logger.debug('ignoring columns outside %d - %d', bstart, bend - 1) # extract a region to average wy = (rinput.average_box_row_size // 2) wx = (rinput.average_box_col_size // 2) self.logger.debug('extraction window is %d rows, %d cols', 2 * wy + 1, 2 * wx + 1) # Fit the peak with these points wfit = 2 * (rinput.fit_peak_npoints // 2) + 1 self.logger.debug('fit with %d points', wfit) # Minimum threshold threshold = 5 * EMIR_RON # Savitsky and Golay (1964) filter to compute the X derivative # scipy >= xx has a savgol_filter function # for compatibility we do it manually allpos = {} ypos3_kernel = None slits = numpy.zeros((EMIR_NBARS, 8), dtype='float') self.logger.info('start finding bars') for ks in [3, 5, 7, 9]: self.logger.debug('kernel size is %d', ks) # S and G kernel for derivative kw = ks * (ks * ks - 1) / 12.0 coeffs_are = -numpy.arange((1 - ks) // 2, (ks - 1) // 2 + 1) / kw if ks == 3: ypos3_kernel = coeffs_are self.logger.debug('kernel weights are %s', coeffs_are) self.logger.debug('derive image in X direction') arr_deriv = convolve1d(arr_median, coeffs_are, axis=-1) # Axis 0 is # self.logger.debug('derive image in Y direction (with kernel=3)') arr_deriv_alt = convolve1d(arr_median_alt, ypos3_kernel, axis=0) positions = [] for coords in barstab: lbarid = int(coords[0]) rbarid = lbarid + EMIR_NBARS ref_y_coor = coords[1] + vec[1] poly_coeffs = coords[2:] prow = coor_to_pix_1d(ref_y_coor) - 1 fits_row = prow + 1 # FITS pixel index # A function that returns the center of the bar # given its X position def center_of_bar(x): # Pixel values are 0-based return polyval(x + 1 - vec[0], poly_coeffs) + vec[1] - 1 self.logger.debug('looking for bars with ids %d - %d', lbarid, rbarid) self.logger.debug('reference y position is Y %7.2f', ref_y_coor) # if ref_y_coor is outlimits, skip this bar # ref_y_coor is in FITS format if (ref_y_coor >= 2047) or (ref_y_coor <= 1): self.logger.debug( 'reference y position is outlimits, skipping') positions.append([lbarid, fits_row, fits_row, 1, 0, 3]) positions.append([rbarid, fits_row, fits_row, 1, 0, 3]) continue # Left bar self.logger.debug('measure left border (%d)', lbarid) centery, xpos, fwhm, st = char_bar_peak_l(arr_deriv, prow, bstart, bend, threshold, center_of_bar, wx=wx, wy=wy, wfit=wfit) xpos1 = xpos positions.append( [lbarid, centery + 1, fits_row, xpos + 1, fwhm, st]) # Right bar self.logger.debug('measure rigth border (%d)', rbarid) centery, xpos, fwhm, st = char_bar_peak_r(arr_deriv, prow, bstart, bend, threshold, center_of_bar, wx=wx, wy=wy, wfit=wfit) positions.append( [rbarid, centery + 1, fits_row, xpos + 1, fwhm, st]) xpos2 = xpos # if st == 0: self.logger.debug('measure top-bottom borders') try: y1, y2, statusy = char_bar_height(arr_deriv_alt, xpos1, xpos2, centery, threshold, wh=35, wfit=wfit) except Exception as error: self.logger.warning('Error computing height: %s', error) statusy = 44 if statusy in [0, 40]: # Main border is detected positions[-1][1] = y2 + 1 positions[-2][1] = y2 + 1 else: # Update status positions[-1][-1] = 4 positions[-2][-1] = 4 else: self.logger.debug('slit is not complete') y1, y2 = 0, 0 # Update positions self.logger.debug( 'bar %d centroid-y %9.4f, row %d x-pos %9.4f, FWHM %6.3f, status %d', *positions[-2]) self.logger.debug( 'bar %d centroid-y %9.4f, row %d x-pos %9.4f, FWHM %6.3f, status %d', *positions[-1]) if ks == 5: slits[lbarid - 1] = [xpos1, y2, xpos2, y2, xpos2, y1, xpos1, y1] # FITS coordinates slits[lbarid - 1] += 1.0 self.logger.debug('inserting bars %d-%d into "slits"', lbarid, rbarid) allpos[ks] = numpy.asarray( positions, dtype='float') # GCS doesn't like lists of lists self.logger.debug('end finding bars') result = self.create_result( frame=hdulist, slits=slits, positions9=allpos[9], positions7=allpos[7], positions5=allpos[5], positions3=allpos[3], DTU=dtub, ROTANG=rotang, TSUTC1=tsutc1, csupos=csupos, csusens=csusens, ) return result
def char_bar_height(arr_deriv_alt, xpos1, xpos2, centery, threshold, wh=35, wfit=3): logger = logging.getLogger('emir.recipes.bardetect') pcentery = coor_to_pix_1d(centery) slicey = slice_create(pcentery, wh, start=1, stop=2047) ref_pcentery = pcentery - slicey.start mm = arr_deriv_alt[slicey, xpos1:xpos2 + 1].mean(axis=-1) idxs_t = find_peaks_indexes(mm, window_width=3, threshold=threshold) idxs_u = find_peaks_indexes(-mm, window_width=3, threshold=threshold) # Peaks on the right status = 0 npeaks_u = len(idxs_u) if npeaks_u == 0: # This is a problem, no peak on the right b2 = 0 status = 4 logger.debug('no bottom border found') else: # Filter over reference g_idxs_u = idxs_u[idxs_u >= ref_pcentery] if len(g_idxs_u) == 0: logger.debug('no peak over center') b2 = 0 status = 4 else: x_u, y_u = refine_peaks(-mm, g_idxs_u, window_width=wfit) # Select the peak with max derivative if len(x_u) == 0 or len(y_u) == 0: logger.warning("no 1st peak found after refine") b2 = 0 status = 4 else: idmax = y_u.argmax() b2 = x_u[idmax] b2val = y_u[idmax] logger.debug('main border in %f', slicey.start + b2) # peaks on the left npeaks_t = len(idxs_t) if npeaks_t == 0: # This is a problem, no peak on the left b1 = 0 logger.debug('no top border found') status = 40 + status else: g_idxs_t = idxs_t[idxs_t <= ref_pcentery] if len(g_idxs_t) == 0: logger.debug('no peak under center') b1 = 0 status = 40 + status else: x_t, y_t = refine_peaks(mm, g_idxs_t, window_width=wfit) # Select the peak with max derivative if len(x_t) == 0 or len(y_t) == 0: logger.warning("no 2nd peak found after refine") b1 = 0 status = 40 + status else: idmax = y_t.argmax() b1 = x_t[idmax] b1val = y_t[idmax] logger.debug('second border in %f', slicey.start + b1) return slicey.start + b1, slicey.start + b2, status
def find_bars(hdulist, bars_nominal_positions, csupos, dtur, average_box_row_size=7, average_box_col_size=21, fit_peak_npoints=3, median_filter_size=5, logger=None): logger.debug('filtering image') # Processed array arr_median = median_filtering(hdulist, median_filter_size) xfac = dtur[0] / EMIR_PIXSCALE yfac = -dtur[1] / EMIR_PIXSCALE vec = [yfac, xfac] logger.debug('DTU shift is %s', vec) # and the table of approx positions of the slits barstab = bars_nominal_positions # Currently, we only use fields 0 and 2 # of the nominal positions file # Number or rows used # These other parameters cab be tuned also bstart = 1 bend = 2047 logger.debug('ignoring columns outside %d - %d', bstart, bend - 1) # extract a region to average # wy = (average_box_row_size // 2) # wx = (average_box_col_size // 2) # logger.debug('extraction window is %d rows, %d cols', 2 * wy + 1, 2 * wx + 1) # Fit the peak with these points # wfit = 2 * (fit_peak_npoints // 2) + 1 # logger.debug('fit with %d points', wfit) # Minimum threshold threshold = 5 * EMIR_RON # Savitsky and Golay (1964) filter to compute the X derivative # scipy >= xx has a savgol_filter function # for compatibility we do it manually allpos = {} slits = numpy.zeros((EMIR_NBARS, 8), dtype='float') logger.info('find peaks in derivative image') for ks in [3, 5, 7, 9]: logger.debug('kernel size is %d', ks) # S and G kernel for derivative kw = ks * (ks * ks - 1) / 12.0 coeffs_are = -numpy.arange((1 - ks) // 2, (ks - 1) // 2 + 1) / kw logger.debug('kernel weights are %s', coeffs_are) logger.debug('derive image in X direction') arr_deriv = convolve1d(arr_median, coeffs_are, axis=-1) # self.save_intermediate_array(arr_deriv, 'deriv_image_k%d.fits' % ks) # Axis 0 is # # logger.debug('derive image in Y direction (with kernel=3)') # arr_deriv_alt = convolve1d(arr_median_alt, ypos3_kernel, axis=0) positions = [] logger.info('using bar parameters') for idx in range(EMIR_NBARS): params_l = barstab[idx] params_r = barstab[idx + EMIR_NBARS] lbarid = int(params_l[0]) # CSUPOS for this bar rbarid = lbarid + EMIR_NBARS current_csupos_l = csupos[lbarid - 1] current_csupos_r = csupos[rbarid - 1] logger.debug('CSUPOS for bar %d is %f', lbarid, current_csupos_l) logger.debug('CSUPOS for bar %d is %f', rbarid, current_csupos_r) ref_y_coor_virt = params_l[1] # Do I need to add vec[1]? ref_x_l_coor_virt = params_l[3] + current_csupos_l * params_l[2] ref_x_r_coor_virt = params_r[3] + current_csupos_r * params_r[2] # Transform to REAL.. ref_x_l_coor, ref_y_l_coor = dist.exvp(ref_x_l_coor_virt, ref_y_coor_virt) ref_x_r_coor, ref_y_r_coor = dist.exvp(ref_x_r_coor_virt, ref_y_coor_virt) # FIXME: check if DTU has to be applied # ref_y_coor = ref_y_coor + vec[1] prow = coor_to_pix_1d(ref_y_l_coor) - 1 fits_row = prow + 1 # FITS pixel index logger.debug('looking for bars with ids %d - %d', lbarid, rbarid) logger.debug('ref Y virtual position is %7.2f', ref_y_coor_virt) logger.debug('ref X virtual positions are %7.2f %7.2f', ref_x_l_coor_virt, ref_x_r_coor_virt) logger.debug('ref X positions are %7.2f %7.2f', ref_x_l_coor, ref_x_r_coor) logger.debug('ref Y positions are %7.2f %7.2f', ref_y_l_coor, ref_y_r_coor) # if ref_y_coor is outlimits, skip this bar # ref_y_coor is in FITS format if (ref_y_l_coor >= 2047 + 16) or (ref_y_l_coor <= 1 - 16): logger.debug('reference y position is outlimits, skipping') positions.append([lbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) positions.append([rbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) continue # minimal width of the slit minwidth = 0.9 if abs(ref_x_l_coor_virt - ref_x_r_coor_virt) < minwidth: # FIXME: if width < minwidth fit peak in image logger.debug('slit is less than %d virt pixels, skipping', minwidth) positions.append([lbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) positions.append([rbarid, fits_row, fits_row, fits_row, 1, 1, 0, 3]) continue # Left bar # Dont add +1 to virtual pixels logger.debug('measure left border (%d)', lbarid) regionw = 10 bstart1 = coor_to_pix_1d(ref_x_l_coor - regionw) bend1 = coor_to_pix_1d(ref_x_l_coor + regionw) + 1 centery, centery_virt, xpos1, xpos1_virt, fwhm, st = char_bar_peak_l(arr_deriv, prow, bstart1, bend1, threshold) insert1 = [lbarid, centery + 1, centery_virt, fits_row, xpos1 + 1, xpos1_virt, fwhm, st] positions.append(insert1) # Right bar # Dont add +1 to virtual pixels logger.debug('measure rigth border (%d)', rbarid) bstart2 = coor_to_pix_1d(ref_x_r_coor - regionw) bend2 = coor_to_pix_1d(ref_x_r_coor + regionw) + 1 centery, centery_virt, xpos2, xpos2_virt, fwhm, st = char_bar_peak_r(arr_deriv, prow, bstart2, bend2, threshold) # This centery/centery_virt should be equal to ref_y_coor_virt insert2 = [rbarid, centery + 1, centery_virt, fits_row, xpos2 + 1, xpos2_virt, fwhm, st] positions.append(insert2) # FIXME: hardcoded value y1_virt = ref_y_coor_virt - 16.242 y2_virt = ref_y_coor_virt + 16.242 _, y1 = dist.exvp(xpos1_virt + 1, y1_virt + 1) _, y2 = dist.exvp(xpos2_virt + 1, y2_virt + 1) # Update positions msg = 'bar %d, centroid-y %9.4f centroid-y virt %9.4f, ' \ 'row %d, x-pos %9.4f x-pos virt %9.4f, FWHM %6.3f, status %d' logger.debug(msg, *positions[-2]) logger.debug(msg, *positions[-1]) if ks == 5: slits[lbarid - 1] = numpy.array([xpos1, y2, xpos2, y2, xpos2, y1, xpos1, y1]) # FITS coordinates slits[lbarid - 1] += 1.0 logger.debug('inserting bars %d-%d into "slits"', lbarid, rbarid) allpos[ks] = numpy.asarray(positions, dtype='float') # GCS doesn't like lists of lists return allpos, slits
def char_bar_height(arr_deriv_alt, xpos1, xpos2, centery, threshold, wh=35, wfit=3): logger = logging.getLogger('emir.recipes.bardetect') pcentery = coor_to_pix_1d(centery) slicey = slice_create(pcentery, wh, start=1, stop=2047) ref_pcentery = pcentery - slicey.start mm = arr_deriv_alt[slicey, xpos1:xpos2 + 1].mean(axis=-1) idxs_t = find_peaks_indexes(mm, window_width=3,threshold=threshold) idxs_u = find_peaks_indexes(-mm, window_width=3,threshold=threshold) # Peaks on the right status = 0 npeaks_u = len(idxs_u) if npeaks_u == 0: # This is a problem, no peak on the right b2 = 0 status = 4 logger.debug('no bottom border found') else: # Filter over reference g_idxs_u = idxs_u[idxs_u >= ref_pcentery] if len(g_idxs_u) == 0: logger.debug('no peak over center') b2 = 0 status = 4 else: x_u, y_u = refine_peaks(-mm, g_idxs_u, window_width=wfit) # Select the peak with max derivative if len(x_u) == 0 or len(y_u) == 0: logger.warning("no 1st peak found after refine") b2 = 0 status = 4 else: idmax = y_u.argmax() b2 = x_u[idmax] b2val = y_u[idmax] logger.debug('main border in %f', slicey.start + b2) # peaks on the left npeaks_t = len(idxs_t) if npeaks_t == 0: # This is a problem, no peak on the left b1 = 0 logger.debug('no top border found') status = 40 + status else: g_idxs_t = idxs_t[idxs_t <= ref_pcentery] if len(g_idxs_t) == 0: logger.debug('no peak under center') b1 = 0 status = 40 + status else: x_t, y_t = refine_peaks(mm, g_idxs_t, window_width=wfit) # Select the peak with max derivative if len(x_t) == 0 or len(y_t) == 0: logger.warning("no 2nd peak found after refine") b1 = 0 status = 40 + status else: idmax = y_t.argmax() b1 = x_t[idmax] b1val = y_t[idmax] logger.debug('second border in %f', slicey.start + b1) return slicey.start + b1, slicey.start + b2, status