/* $Id: cpl_mask.c,v 1.35 2007/11/22 08:41:59 yjung Exp $ * * This file is part of the ESO Common Pipeline Library * Copyright (C) 2001-2004 European Southern Observatory * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * $Author: yjung $ * $Date: 2007/11/22 08:41:59 $ * $Revision: 1.35 $ * $Name: $ */ #ifdef HAVE_CONFIG_H #include #endif /*----------------------------------------------------------------------------- Includes -----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include "cpl_error.h" #include "cpl_image_io.h" #include "cpl_memory.h" #include "cpl_mask.h" #include "cpl_image_bpm.h" #include "cpl_image_defs.h" /*----------------------------------------------------------------------------*/ /** * @defgroup cpl_mask Masks of pixels * * This module provides functions to handle masks of pixels * * These masks are useful for object detection routines or bad pixel map * handling. Morphological routines (erosion, dilation, closing and * opening) and logical operations are provided. A cpl_mask is a kind of * binary array whose elements are of type cpl_binary and can take only * two values: either CPL_BINARY_0 or CPL_BINARY_1. * * @par Synopsis: * @code * #include "cpl_mask.h" * @endcode */ /*----------------------------------------------------------------------------*/ /**@{*/ /*----------------------------------------------------------------------------- Type definition -----------------------------------------------------------------------------*/ struct _cpl_mask_ { int nx ; int ny ; cpl_binary * data ; } ; /*----------------------------------------------------------------------------- Function codes -----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ /** @brief Create a new cpl_mask @param nx number of element in x direction @param ny number of element in y direction @return 1 newly allocated cpl_mask or NULL in error case The created cpl_mask elements are all set to CPL_BINARY_0. The returned object must be deallocated using cpl_mask_delete(). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_ILLEGAL_INPUT if nx or ny is negative */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_mask_new(int nx, int ny) { cpl_mask * m ; /* Test input */ cpl_ensure(nx>0 && ny>0, CPL_ERROR_ILLEGAL_INPUT, NULL) ; /* Allocate memory */ m = cpl_malloc(sizeof(cpl_mask)) ; m->nx = nx ; m->ny = ny ; m->data = cpl_calloc(nx*ny, sizeof(cpl_binary)) ; /* Return */ return m ; } /*----------------------------------------------------------------------------*/ /** @brief Create a cpl_mask from existing data @param nx number of element in x direction @param ny number of element in y direction @param data Pointer to array of nx*ny cpl_binary @return 1 newly allocated cpl_mask or NULL in case of an error The returned object must be deallocated using cpl_mask_unwrap(). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if an input pointer is NULL - CPL_ERROR_ILLEGAL_INPUT if nx or ny is negative or zero */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_mask_wrap(int nx, int ny, cpl_binary * data) { cpl_mask * m; /* Test input */ cpl_ensure(data, CPL_ERROR_NULL_INPUT, NULL); cpl_ensure(nx > 0 && ny > 0, CPL_ERROR_ILLEGAL_INPUT, NULL); /* Allocate memory */ m = cpl_malloc(sizeof(cpl_mask)); m->nx = nx ; m->ny = ny ; m->data = data; /* Return */ return m ; } /*----------------------------------------------------------------------------*/ /** @brief Duplicates a cpl_mask @param in the mask to duplicate @return 1 newly allocated cpl_mask or NULL in error case The returned object must be deallocated using cpl_mask_delete(). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if in is NULL */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_mask_duplicate(const cpl_mask * in) { cpl_mask * out ; int nx, ny ; /* Test input */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, NULL) ; nx = cpl_mask_get_size_x(in) ; ny = cpl_mask_get_size_y(in) ; /* Create the output mask */ out = cpl_mask_new(nx, ny) ; memcpy(out->data, in->data, nx*ny*sizeof(cpl_binary)) ; /* Return */ return out ; } /*----------------------------------------------------------------------------*/ /** @brief Delete a cpl_mask @param m cpl_mask to delete @return void */ /*----------------------------------------------------------------------------*/ void cpl_mask_delete(cpl_mask * m) { if (m == NULL) return ; if (m->data != NULL) cpl_free(m->data) ; cpl_free(m) ; return ; } /*----------------------------------------------------------------------------*/ /** @brief Delete a cpl_mask except the data array @param m cpl_mask to delete @return A pointer to the data array or NULL if the input is NULL. @note The data array must subsequently be deallocated. Failure to do so will result in a memory leak. */ /*----------------------------------------------------------------------------*/ void * cpl_mask_unwrap(cpl_mask * m) { void * data; if (m == NULL) return NULL; data = (void *) m->data; cpl_free(m); return data; } /*----------------------------------------------------------------------------*/ /** @brief Get a pointer to the data part of the mask @param in the input mask @return Pointer to the data or NULL in error case The returned pointer refers to already allocated data. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ cpl_binary * cpl_mask_get_data(cpl_mask * in) { cpl_ensure(in, CPL_ERROR_NULL_INPUT, NULL) ; return in->data ; } /*----------------------------------------------------------------------------*/ /** @brief Get a pointer to the data part of the mask @param in the input mask @return Pointer to the data or NULL in error case @see cpl_mask_get_data */ /*----------------------------------------------------------------------------*/ const cpl_binary * cpl_mask_get_data_const(const cpl_mask * in) { return cpl_mask_get_data((cpl_mask *)in); } /*----------------------------------------------------------------------------*/ /** @brief Get the value of a mask at a given position @param in the input mask @param xpos x position (FITS convention) @param ypos y position (FITS convention) @return The mask value or undefined if an error code is set The mask value can be either CPL_BINARY_0 or CPL_BINARY_1 Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if xpos or ypos is out of bounds */ /*----------------------------------------------------------------------------*/ cpl_binary cpl_mask_get( const cpl_mask * in, int xpos, int ypos) { int nx, ny ; /* Test entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, CPL_BINARY_0) ; nx = cpl_mask_get_size_x(in) ; ny = cpl_mask_get_size_y(in) ; cpl_ensure(xpos>0 && xpos<=nx, CPL_ERROR_ILLEGAL_INPUT,CPL_BINARY_0) ; cpl_ensure(ypos>0 && ypos<=ny, CPL_ERROR_ILLEGAL_INPUT,CPL_BINARY_0) ; return in->data[(xpos-1)+(ypos-1)*nx] ; } /*----------------------------------------------------------------------------*/ /** @brief Set a value in a mask at a given position @param in the input mask @param xpos x position (FITS convention) @param ypos y position (FITS convention) @param value the value to set in the mask @return the #_cpl_error_code_ or CPL_ERROR_NONE The value can be either CPL_BINARY_0 or CPL_BINARY_1 Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if xpos or ypos is out of bounds or if value is different from CPL_BINARY_0 and CPL_BINARY_1 */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_set( cpl_mask * in, int xpos, int ypos, cpl_binary value) { int nx, ny ; /* Test entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, CPL_BINARY_0) ; nx = cpl_mask_get_size_x(in) ; ny = cpl_mask_get_size_y(in) ; cpl_ensure(xpos>0 && xpos<=nx, CPL_ERROR_ILLEGAL_INPUT,CPL_BINARY_0) ; cpl_ensure(ypos>0 && ypos<=ny, CPL_ERROR_ILLEGAL_INPUT,CPL_BINARY_0) ; cpl_ensure(value==CPL_BINARY_0 || value==CPL_BINARY_1, CPL_ERROR_ILLEGAL_INPUT, CPL_BINARY_0) ; in->data[(xpos-1)+(ypos-1)*nx] = value ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Get the x size of the mask @param in the input mask @return The mask x size, or -1 on NULL input Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ int cpl_mask_get_size_x(const cpl_mask * in) { /* Test entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, -1) ; return in->nx ; } /*----------------------------------------------------------------------------*/ /** @brief Get the y size of the mask @param in the input mask @return The mask y size, or -1 on NULL input Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ int cpl_mask_get_size_y(const cpl_mask * in) { /* Test entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, -1) ; return in->ny ; } /*----------------------------------------------------------------------------*/ /** @brief Return CPL_TRUE iff a mask has no elements set (to CPL_BINARY_1) @param self The mask to search @return CPL_TRUE iff the mask has no elements set (to CPL_BINARY_1) @see cpl_mask_is_empty_window() Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if an input pointer is NULL */ /*----------------------------------------------------------------------------*/ cpl_boolean cpl_mask_is_empty(const cpl_mask * self) { cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, CPL_FALSE); return cpl_mask_is_empty_window(self, 1, 1, self->nx, self->ny); } /*----------------------------------------------------------------------------*/ /** @brief Return CPL_TRUE iff a mask has no elements set in the window @param self The mask to search @param llx Lower left x position (FITS convention) @param lly Lower left y position (FITS convention) @param urx Upper right x position (FITS convention) @param ury Upper right y position (FITS convention) @return CPL_TRUE iff the mask has no elements set (to CPL_BINARY_1) Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if an input pointer is NULL - CPL_ERROR_ILLEGAL_INPUT if the window coordinates are not valid */ /*----------------------------------------------------------------------------*/ cpl_boolean cpl_mask_is_empty_window(const cpl_mask * self, int llx, int lly, int urx, int ury) { const cpl_binary * pi; cpl_boolean result; cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, CPL_FALSE); cpl_ensure(llx <= urx, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE); cpl_ensure(llx > 0, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE); cpl_ensure(urx <= self->nx, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE); cpl_ensure(lly <= ury, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE); cpl_ensure(lly > 0, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE); cpl_ensure(ury <= self->ny, CPL_ERROR_ILLEGAL_INPUT, CPL_FALSE); /* Point to 1st element in first row to read */ pi = self->data + (lly-1)*self->nx; if (llx == 1 && urx == self->nx) { result = memchr(pi, CPL_BINARY_1, (size_t)(self->nx * (ury-lly+1)) * sizeof(*pi)) ? CPL_FALSE : CPL_TRUE; } else { const size_t rowsize = (size_t)(urx-llx+1) * sizeof(*pi); int j; /* Point to first element to read */ pi += llx - 1; for (j = lly - 1; j < ury; j++, pi += self->nx) { if (memchr(pi, CPL_BINARY_1, rowsize)) break; } result = j < ury ? CPL_FALSE : CPL_TRUE; } return result; } /*----------------------------------------------------------------------------*/ /** @brief Get the number of occurences of CPL_BINARY_1 @param in the input mask @return the number of occurences of CPL_BINARY_1 or -1 in error case Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ int cpl_mask_count(const cpl_mask * in) { int nb_selected ; int i ; /* Test inputs */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, -1) ; return cpl_mask_count_window(in, 1, 1, in->nx, in->ny) ; } /*----------------------------------------------------------------------------*/ /** @brief Get the number of occurences of CPL_BINARY_1 in a window @param in the input mask @param llx Lower left x position (FITS convention) @param lly Lower left y position (FITS convention) @param urx Upper right x position (FITS convention) @param ury Upper right y position (FITS convention) @return the number of occurences of CPL_BINARY_1 or -1 in error case Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the window coordinates are not valid */ /*----------------------------------------------------------------------------*/ int cpl_mask_count_window(const cpl_mask * in, int llx, int lly, int urx, int ury) { int nb_selected ; const cpl_binary * pi; int i, j ; /* Test inputs */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, -1) ; cpl_ensure(llx <= urx, CPL_ERROR_ILLEGAL_INPUT, -1); cpl_ensure(llx > 0, CPL_ERROR_ILLEGAL_INPUT, -1); cpl_ensure(urx <= in->nx, CPL_ERROR_ILLEGAL_INPUT, -1); cpl_ensure(lly <= ury, CPL_ERROR_ILLEGAL_INPUT, -1); cpl_ensure(lly > 0, CPL_ERROR_ILLEGAL_INPUT, -1); cpl_ensure(ury <= in->ny, CPL_ERROR_ILLEGAL_INPUT, -1); /* Initialise */ pi = in->data + (lly-1)*in->nx; nb_selected = 0 ; for (j=lly-1 ; jdata)[i+j*in->nx] == CPL_BINARY_1) nb_selected++ ; } } return nb_selected ; } /*----------------------------------------------------------------------------*/ /** @brief Performs a logical AND between two masks @param in1 first mask @param in2 second mask @return the #_cpl_error_code_ or CPL_ERROR_NONE The result is stored in the first mask. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the input masks have different sizes */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_and( cpl_mask * in1, const cpl_mask * in2) { int i ; /* Test entries */ cpl_ensure_code(in1 && in2, CPL_ERROR_NULL_INPUT) ; cpl_ensure_code(in1->nx==in2->nx && in1->ny==in2->ny, CPL_ERROR_ILLEGAL_INPUT); for (i=0 ; inx*in1->ny ; i++) (in1->data)[i] = (cpl_binary)((int)(in1->data)[i]&(int)(in2->data)[i]) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Performs a logical OR between two masks @param in1 first mask @param in2 second mask @return the #_cpl_error_code_ or CPL_ERROR_NONE @see cpl_mask_and() */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_or( cpl_mask * in1, const cpl_mask * in2) { int i ; /* Test entries */ cpl_ensure_code(in1 && in2, CPL_ERROR_NULL_INPUT) ; cpl_ensure_code(in1->nx==in2->nx && in1->ny==in2->ny, CPL_ERROR_ILLEGAL_INPUT); for (i=0 ; inx*in1->ny ; i++) (in1->data)[i] = (cpl_binary)((int)(in1->data)[i]|(int)(in2->data)[i]) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Performs a logical XOR between two masks @param in1 first mask @param in2 second mask @return the #_cpl_error_code_ or CPL_ERROR_NONE @see cpl_mask_and() */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_xor( cpl_mask * in1, const cpl_mask * in2) { int i ; /* Test entries */ cpl_ensure_code(in1 && in2, CPL_ERROR_NULL_INPUT) ; cpl_ensure_code(in1->nx==in2->nx && in1->ny==in2->ny, CPL_ERROR_ILLEGAL_INPUT) ; for (i=0 ; inx*in1->ny ; i++) (in1->data)[i] = (cpl_binary)((int)(in1->data)[i]^(int)(in2->data)[i]) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Performs a logical NOT on a mask @param in input mask @return the #_cpl_error_code_ or CPL_ERROR_NONE Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_not(cpl_mask * in) { int i ; /* Test entries */ cpl_ensure_code(in, CPL_ERROR_NULL_INPUT) ; for (i=0 ; inx*in->ny ; i++) (in->data)[i] = (cpl_binary)(!(int)(in->data)[i]) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Collapse a mask @param in input mask to collapse @param dir collapsing direction @return the newly allocated mask or NULL in error case The returned mask must be deallocated using cpl_mask_delete(). direction 0 is to collapse along y (sum of rows), 1 is along x. The resulting mask element is set to 1 if all element of the associated column (resp. row) in the input mask are set to 1. @verbatim Collapse along y: 1 0 1 Input mask. 0 1 1 0 0 1 ----- 0 0 1 The third element is flagged as bad because all elements of the column are bad. @endverbatim Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT - CPL_ERROR_ILLEGAL_INPUT */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_mask_collapse_create( const cpl_mask * in, int dir) { const cpl_binary* pin ; cpl_mask * out ; cpl_binary * pout ; int i, j ; /* Check entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, NULL) ; /* Get access to bpm_in */ pin = cpl_mask_get_data_const(in) ; /* Check the collapsing direction */ if (dir == 0) { out = cpl_mask_new(in->nx, 1) ; pout = cpl_mask_get_data(out) ; for (i=0 ; inx ; i++) { for (j=0 ; jny ; j++) if (pin[i+j*in->nx] == CPL_BINARY_0) break; if (j == in->ny) pout[i] = CPL_BINARY_1; } } else if (dir == 1) { out = cpl_mask_new(1, in->ny) ; pout = cpl_mask_get_data(out) ; for (i=0 ; iny ; i++) { for (j=0 ; jnx ; j++) if (pin[j+i*in->nx] == CPL_BINARY_0) break; if (j == in->nx) pout[i] = CPL_BINARY_1; } } else { out = NULL; (void)cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT); } /* out is NULL if dir is illegal */ return out ; } /*----------------------------------------------------------------------------*/ /** @brief Extract a mask from an other one @param in input mask @param llx coordinates of the extracted mask @param lly @param urx @param ury @return 1 newly allocated mask or NULL in error case. The returned mask must be deallocated using cpl_mask_delete(). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_ILLEGAL_INPUT if the zone falls outside the mask - CPL_ERROR_NULL_INPUT if the input mask is NULL */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_mask_extract( const cpl_mask * in, int llx, int lly, int urx, int ury) { cpl_mask * out ; int lx, ly ; const cpl_binary* pin ; cpl_binary * pout ; int pos ; int i, j ; /* Test entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, NULL) ; cpl_ensure(llx>0 && llx<=in->nx && lly>0 && lly<=in->ny && urx>0 && urx<=in->nx && ury>0 && ury<=in->ny && llx<=urx && lly<=ury, CPL_ERROR_ILLEGAL_INPUT, NULL) ; /* Create the new mask */ lx = urx - llx + 1 ; ly = ury - lly + 1 ; out = cpl_mask_new(lx, ly) ; pout = cpl_mask_get_data(out) ; pin = cpl_mask_get_data_const(in) ; /* Loop on the zone */ for (j=lly-1 ; jnx] ; } } return out ; } /*----------------------------------------------------------------------------*/ /** @brief Rotate a mask by a multiple of 90 degrees counterclockwise. @param self Mask to rotate in place @param rot The multiple: -1 is a rotation of 90 deg clockwise. @return CPL_ERROR_NONE on success, otherwise the relevant #_cpl_error_code_ @see cpl_image_turn() rot may be any integer value, its modulo 4 determines the rotation: - -3 to turn 270 degrees clockwise. - -2 to turn 180 degrees clockwise. - -1 to turn 90 degrees clockwise. - 0 to not turn - +1 to turn 90 degrees counterclockwise (same as -3) - +2 to turn 180 degrees counterclockwise (same as -2). - +3 to turn 270 degrees counterclockwise (same as -1). Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if self is NULL */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_turn(cpl_mask * self, int rot) { cpl_mask * loc; const cpl_binary * ploc; cpl_binary * pself; int i, j; /* Check entries */ cpl_ensure_code(self, CPL_ERROR_NULL_INPUT); rot %= 4; if (rot < 0) rot += 4; /* Create the local mask */ loc = cpl_mask_duplicate(self); ploc = cpl_mask_get_data_const(loc); pself = cpl_mask_get_data(self); /* Compute the new positions */ /* Switch on the kind of rotation */ /* rot is 0, 1, 2 or 3. */ switch (rot) { case 1: self->nx = loc->ny ; self->ny = loc->nx ; pself += ((self->ny)-1)* (self->nx) ; for (j=0 ; j<(self->nx) ; j++) { for (i=0 ; i<(self->ny) ; i++) { *pself = *ploc++ ; pself -= (self->nx) ; } pself += (self->nx)*(self->ny)+1 ; } break ; case 2: for (i=0 ; i<(self->nx)*(self->ny) ; i++) pself[i] = ploc[(self->ny)*(self->nx)-1-i] ; break ; case 3: self->nx = loc->ny ; self->ny = loc->nx ; pself += (self->nx)-1 ; for (j=0 ; j<(self->nx) ; j++) { for (i=0 ; i<(self->ny) ; i++) { *pself = *ploc++; pself += (self->nx) ; } pself -= (self->nx)*(self->ny)+1 ; } break ; default: break ; } cpl_mask_delete(loc) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Shift a mask @param in mask to shift @param x_shift shift to apply in x @param y_shift shift to apply in y @return the #_cpl_error_code_ or CPL_ERROR_NONE @see cpl_image_turn() The 'empty zone' in the shifted mask is set as bad pixels. The shift values have to be valid: -nxnx)&&(ys_absny), CPL_ERROR_ILLEGAL_INPUT); /* Handle the case where nothing has to be done */ if (x_shift==0 && y_shift==0) return CPL_ERROR_NONE ; /* Create the local mask */ loc = cpl_mask_duplicate(in) ; ploc = cpl_mask_get_data(loc) ; pin = cpl_mask_get_data(in) ; for (j=0 ; jny ; j++) { for (i=0 ; inx ; i++) { if ((i-x_shift>=0) && (i-x_shiftnx) && (j-y_shift>=0) && (j-y_shiftny)) { pin[i+j*in->nx] = ploc[(i-x_shift)+(j-y_shift)*in->nx] ; } else { pin[i+j*in->nx] = CPL_BINARY_1 ; } } } cpl_mask_delete(loc) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Insert a mask in an other one @param in1 mask in which in2 is inserted @param in2 mask to insert @param x_pos the x pixel position in in1 where the lower left pixel of in2 should go (from 1 to the x size of in1) @param y_pos the y pixel position in in1 where the lower left pixel of in2 should go (from 1 to the y size of in1) @return the #_cpl_error_code_ or CPL_ERROR_NONE Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if in1 or in2 is NULL - CPL_ERROR_ILLEGAL_INPUT if x_pos, y_pos is outside in1 */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_copy( cpl_mask * in1, const cpl_mask * in2, int x_pos, int y_pos) { int urx, ury ; cpl_binary * pin1 ; const cpl_binary* pin2 ; int im1_pos, im2_pos ; int i, j ; /* Test entries */ cpl_ensure_code(in1 && in2, CPL_ERROR_NULL_INPUT); cpl_ensure_code(x_pos>=1 && x_pos<=in1->nx, CPL_ERROR_ILLEGAL_INPUT); cpl_ensure_code(y_pos>=1 && y_pos<=in1->ny, CPL_ERROR_ILLEGAL_INPUT); /* Define the zone to modify in in1: x_pos, y_pos, urx, ury */ if (x_pos+in2->nx-1 >= in1->nx) urx = in1->nx; else urx = x_pos+in2->nx-1; if (y_pos+in2->ny-1 >= in1->ny) ury = in1->ny; else ury = y_pos+in2->ny-1; /* Get access to the data */ pin1 = cpl_mask_get_data(in1) ; pin2 = cpl_mask_get_data_const(in2) ; /* Loop on the zone */ for (j=y_pos-1 ; jnx ; im2_pos = (i-x_pos+1) + (j-y_pos+1)*in2->nx ; pin1[im1_pos] = pin2[im2_pos] ; } } return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Flip a mask on a given miror line. @param in mask to flip @param angle mirror line in polar coord. is theta = (PI/4) * angle @return the #_cpl_error_code_ or CPL_ERROR_NONE angle can take one of the following values: - 0 (theta=0) to flip the image around the horizontal - 1 (theta=pi/4) to flip the image around y=x - 2 (theta=pi/2) to flip the image around the vertical - 3 (theta=3pi/4) to flip the image around y=-x Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if in is NULL - CPL_ERROR_ILLEGAL_INPUT if angle is not as specified */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_flip( cpl_mask * in, int angle) { cpl_mask * loc ; cpl_binary * ploc ; cpl_binary * pin ; int nx, ny ; int i, j ; /* Check entries */ cpl_ensure_code(in, CPL_ERROR_NULL_INPUT) ; cpl_ensure_code(angle==0 || angle==1 || angle==2 || angle==3, CPL_ERROR_NULL_INPUT) ; /* Create the local mask */ loc = cpl_mask_duplicate(in) ; ploc = cpl_mask_get_data(loc) ; pin = cpl_mask_get_data(in) ; /* Initialise */ nx = in->nx ; ny = in->ny ; /* Compute the new positions */ /* Switch on the kind of flipping */ switch (angle) { case 0: ploc += (ny-1)*nx ; for (j=0 ; jnx = ny ; in->ny = nx ; for (j=0 ; jnx = ny ; in->ny = nx ; ploc += (nx*ny-1) ; for (j=0 ; j 0, CPL_ERROR_ILLEGAL_INPUT) ; cpl_ensure_code(in->nx % nb_cut == 0, CPL_ERROR_ILLEGAL_INPUT) ; cpl_ensure_code(in->ny % nb_cut == 0, CPL_ERROR_ILLEGAL_INPUT) ; /* Initialize */ tile_sz_x = in->nx / nb_cut ; tile_sz_y = in->ny / nb_cut ; /* Create local mask */ loc = cpl_mask_duplicate(in) ; ploc = cpl_mask_get_data(loc) ; pin = cpl_mask_get_data(in) ; /* Loop to move the pixels */ for (j=0 ; jnx*(l+j*tile_sz_y); npos=(k+tile_x*tile_sz_x) + in->nx*(l+tile_y*tile_sz_y); pin[npos] = ploc[opos] ; } } } } cpl_mask_delete(loc) ; /* Return */ return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Subsample a mask @param in input mask @param xstep Take every xstep pixel in x @param ystep Take every ystep pixel in y @return the newly allocated mask or NULL on error case @see cpl_image_extract_subsample() The returned mask must be deallocated using cpl_mask_delete(). Currently xstep must equal ystep. Currently, both the X- and Y-size of the mask must be divisible by the step. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if in is NULL - CPL_ERROR_ILLEGAL_INPUT if xstep and ystep are not as requested. */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_mask_extract_subsample( const cpl_mask * in, int xstep, int ystep) { int new_nx, new_ny ; cpl_mask * out ; cpl_binary * pout ; const cpl_binary* pin ; int pos ; int i, j ; /* Test entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, NULL) ; cpl_ensure(xstep==ystep, CPL_ERROR_ILLEGAL_INPUT, NULL) ; cpl_ensure(xstep>0, CPL_ERROR_ILLEGAL_INPUT, NULL); cpl_ensure(in->nx >= xstep, CPL_ERROR_ILLEGAL_INPUT, NULL); cpl_ensure(in->ny >= xstep, CPL_ERROR_ILLEGAL_INPUT, NULL); cpl_ensure(!(in->nx % xstep), CPL_ERROR_ILLEGAL_INPUT, NULL); cpl_ensure(!(in->ny % xstep), CPL_ERROR_ILLEGAL_INPUT, NULL); /* Initialize */ new_nx = in->nx/xstep ; new_ny = in->ny/xstep ; /* Create the new mask */ out = cpl_mask_new(new_nx, new_ny) ; pout = cpl_mask_get_data(out) ; pin = cpl_mask_get_data_const(in) ; for (j=0 ; jnx ; pout[i+j*new_nx] = pin[pos] ; } } return out ; } /*----------------------------------------------------------------------------*/ /** @brief Compute a morphological opening @param in input mask @param ker binary kernel (0 for 0, any other value is considered as 1) @return the #_cpl_error_code_ or CPL_ERROR_NONE The morphological opening is an erosion followed by a dilation. The input mask is modified. The input kernel should have an odd number of rows and columns. The maximum size of the kernel is 31x31. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the kernel is such that the erosion or the dilation cannot be done */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_opening( cpl_mask * in, const cpl_matrix * ker) { cpl_ensure_code(in && ker, CPL_ERROR_NULL_INPUT) ; if (cpl_mask_erosion(in, ker) != CPL_ERROR_NONE) return CPL_ERROR_ILLEGAL_INPUT ; if (cpl_mask_dilation(in, ker) != CPL_ERROR_NONE) return CPL_ERROR_ILLEGAL_INPUT ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Compute a morphological closing @param in input mask @param ker binary kernel (0 for 0, any other value is considered as 1) @return the #_cpl_error_code_ or CPL_ERROR_NONE The morphological closing is a dilation followed by an erosion. The input mask is modified. The input kernel should have an odd number of rows and columns. The maximum size of the kernel is 31x31. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the kernel is such that the erosion or the dilation cannot be done */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_closing( cpl_mask * in, const cpl_matrix * ker) { cpl_ensure_code(in && ker, CPL_ERROR_NULL_INPUT) ; if (cpl_mask_dilation(in, ker) != CPL_ERROR_NONE) return CPL_ERROR_ILLEGAL_INPUT ; if (cpl_mask_erosion(in, ker) != CPL_ERROR_NONE) return CPL_ERROR_ILLEGAL_INPUT ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Compute a morphological erosion @param in input mask @param ker binary kernel (0 for 0, any other value is considered as 1) @return the #_cpl_error_code_ or CPL_ERROR_NONE The input mask is modified. The input kernel should have an odd number of rows and columns. The maximum size of the kernel is 31x31. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the kernel is not as requested */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_erosion( cpl_mask * in, const cpl_matrix * ker) { cpl_mask * out ; const double * ker_arr ; int nc, nr ; int hsx, hsy ; int curr_pos, im_pos, filt_pos; int i, j, k, l ; /* Test entries */ cpl_ensure_code(in && ker, CPL_ERROR_NULL_INPUT) ; /* Get kernel informations */ nr = cpl_matrix_get_nrow(ker) ; nc = cpl_matrix_get_ncol(ker) ; ker_arr = cpl_matrix_get_data_const(ker) ; /* Test the kernel validity */ cpl_ensure_code(nc%2 && nr%2, CPL_ERROR_ILLEGAL_INPUT) ; cpl_ensure_code(nc<=31 && nr<=31, CPL_ERROR_ILLEGAL_INPUT) ; /* Initialise */ hsx = (nc-1) / 2 ; hsy = (nr-1) / 2 ; /* Create a tmp mask */ out = cpl_mask_new(in->nx, in->ny) ; /* Main filter loop */ for (j=0 ; jny ; j++) { for (i=0 ; inx ; i++) { /* Curent pixel position */ curr_pos = i + j*in->nx ; /* Edges are not computed */ if ((i=in->nx-hsx) || (j=in->ny-hsy)) { out->data[curr_pos] = CPL_BINARY_0 ; } else { /* Initialise */ out->data[curr_pos] = CPL_BINARY_1 ; /* Go into upper left corner of current pixel */ im_pos = curr_pos - hsx + hsy*in->nx ; filt_pos = 0 ; for (k=0 ; kdata)[im_pos] == CPL_BINARY_0) && (fabs(ker_arr[filt_pos]) > FLT_MIN)) (out->data)[curr_pos] = CPL_BINARY_0 ; /* Next col */ filt_pos++ ; im_pos++ ; } /* Next row */ im_pos -= in->nx + nc ; } } } } memcpy(in->data, out->data, in->nx * in->ny * sizeof(cpl_binary)) ; cpl_mask_delete(out) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Compute a morphological dilation @param in input mask @param ker binary kernel (0 for 0, any other value is considered as 1) @return the #_cpl_error_code_ or CPL_ERROR_NONE The input mask is modified. The input kernel should have an odd number of rows and columns. The maximum size of the kernel is 31x31. Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL - CPL_ERROR_ILLEGAL_INPUT if the kernel is not as requested */ /*----------------------------------------------------------------------------*/ cpl_error_code cpl_mask_dilation( cpl_mask * in, const cpl_matrix * ker) { cpl_mask * out ; const double * ker_arr ; int nc, nr ; int hsx, hsy ; int curr_pos, im_pos, filt_pos; int i, j, k, l ; /* Test entries */ cpl_ensure_code(in && ker, CPL_ERROR_NULL_INPUT) ; /* Get kernel informations */ nr = cpl_matrix_get_nrow(ker) ; nc = cpl_matrix_get_ncol(ker) ; ker_arr = cpl_matrix_get_data_const(ker) ; /* Test the kernel validity */ cpl_ensure_code(nc%2 && nr%2, CPL_ERROR_ILLEGAL_INPUT) ; cpl_ensure_code(nc<=31 && nr<=31, CPL_ERROR_ILLEGAL_INPUT) ; /* Initialise */ hsx = (nc-1) / 2 ; hsy = (nr-1) / 2 ; /* Create a tmp binary image */ out = cpl_mask_new(in->nx, in->ny) ; /* Main filter loop */ for (j=0 ; jny ; j++) { for (i=0 ; inx ; i++) { /* Curent pixel position */ curr_pos = i + j*in->nx ; /* Edges are not computed */ if ((i=in->nx-hsx) || (j=in->ny-hsy)) { (out->data)[curr_pos] = CPL_BINARY_0 ; } else { /* Initialise */ (out->data)[curr_pos] = CPL_BINARY_0 ; /* Go into upper left corner of current pixel */ im_pos = curr_pos - hsx + hsy*in->nx ; filt_pos = 0 ; for (k=0 ; kdata)[im_pos] == CPL_BINARY_1) && (fabs(ker_arr[filt_pos]) > FLT_MIN)) (out->data)[curr_pos] = CPL_BINARY_1 ; /* Next col */ filt_pos++ ; im_pos++ ; } /* Next row */ im_pos -= in->nx + nc ; } } } } memcpy(in->data, out->data, in->nx * in->ny * sizeof(cpl_binary)) ; cpl_mask_delete(out) ; return CPL_ERROR_NONE ; } /*----------------------------------------------------------------------------*/ /** @brief Select parts of an image with provided thresholds @param in Image to threshold. @param lo_cut Lower bound for threshold. @param hi_cut Higher bound for threshold. @return 1 newly allocated mask or NULL in error case Create a mask from an image. Selected areas are the ones strictly inside the provided interval. The input image type can be CPL_TYPE_DOUBLE, CPL_TYPE_FLOAT or CPL_TYPE_INT. The returned mask must be deallocated with cpl_mask_delete() Possible #_cpl_error_code_ set in this function: - CPL_ERROR_NULL_INPUT if (one of) the input pointer(s) is NULL */ /*----------------------------------------------------------------------------*/ cpl_mask * cpl_mask_threshold_image_create( const cpl_image * in, double lo_cut, double hi_cut) { cpl_mask * out ; double * pdi ; float * pfi ; int * pii ; int i ; /* Test entries */ cpl_ensure(in, CPL_ERROR_NULL_INPUT, NULL) ; /* Create output object */ out = cpl_mask_new(in->nx, in->ny) ; /* Switch on the input data type */ switch (in->type) { case CPL_TYPE_DOUBLE: pdi = (double*)in->pixels ; for (i=0 ; i<(out->nx * out->ny) ; i++) { if ((pdi[i]>lo_cut) && (pdi[i]data)[i] = CPL_BINARY_1 ; else (out->data)[i] = CPL_BINARY_0 ; } break ; case CPL_TYPE_FLOAT: pfi = (float*)in->pixels ; for (i=0 ; i<(out->nx * out->ny) ; i++) { if ((pfi[i]>lo_cut) && (pfi[i]data)[i] = CPL_BINARY_1 ; else (out->data)[i] = CPL_BINARY_0 ; } break ; case CPL_TYPE_INT: pii = (int*)in->pixels ; for (i=0 ; i<(out->nx * out->ny) ; i++) { if ((pii[i]>lo_cut) && (pii[i]data)[i] = CPL_BINARY_1 ; else (out->data)[i] = CPL_BINARY_0 ; } break ; default: cpl_mask_delete(out) ; out = NULL ; } return out ; } /**@}*/