From cfeb35ab5f90be8b3b82bf5a050eebbb89c20d08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Satabin?= Date: Tue, 13 Jan 2026 11:12:17 +0100 Subject: [PATCH] Ajout du moteur de calcul d'image SubsampledImage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cette classe fille d'Image permet de moyenner les pixels de l'image en entrée, en donnant la taille en X et Y de l'emprise de la moyenne. --- CHANGELOG.md | 3 + include/rok4/image/Image.h | 262 +++++++++++++-------------- include/rok4/image/SubsampledImage.h | 173 ++++++++++++++++++ src/image/SubsampledImage.cpp | 171 +++++++++++++++++ 4 files changed, 477 insertions(+), 132 deletions(-) create mode 100644 include/rok4/image/SubsampledImage.h create mode 100644 src/image/SubsampledImage.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f91eeb..52fbb26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ Le format est basé sur [Keep a Changelog](https://keepachangelog.com/) et ce pr ## [Unreleased] ### Added + +- `SubsampledImage` : cette classe fille d'Image permet de moyenner les pixels de l'image en entrée, en donnant la taille en X et Y de l'emprise de la moyenne. + ### Changed - `Cache` : Export de toutes les classes implémentées dans Cache dans leurs propres fichiers. Les fichiers ajoutés sont : diff --git a/include/rok4/image/Image.h b/include/rok4/image/Image.h index 495391a..3ea8ae5 100644 --- a/include/rok4/image/Image.h +++ b/include/rok4/image/Image.h @@ -47,13 +47,12 @@ #include #include -#include #include +#include #include "rok4/utils/BoundingBox.h" #include "rok4/utils/CrsBook.h" - #define METER_PER_DEG 111319.492 /** @@ -72,8 +71,7 @@ */ class Image { -protected: - + protected: /** * \~french \brief Nombre de canaux de l'image * \~english \brief Number of samples per pixel @@ -133,12 +131,11 @@ class Image { * \brief Resolutions calculation, from pixel size and bounding box */ void compute_resolutions() { - resx = ( bbox.xmax - bbox.xmin ) / double ( width ); - resy = ( bbox.ymax - bbox.ymin ) / double ( height ); + resx = (bbox.xmax - bbox.xmin) / double(width); + resy = (bbox.ymax - bbox.ymin) / double(height); } -public: - + public: /** * \~french * \brief Vérifie la cohérence des dimensions d'image fournies @@ -155,21 +152,23 @@ class Image { * \param[in] h Image's height, in pixel * \param[in] bounding_box Image's bounding box */ - static bool are_dimensions_consistent(double resolution_x, double resolution_y, int w, int h, BoundingBox bounding_box) { + static bool are_dimensions_consistent(double resolution_x, double resolution_y, int w, int h, + BoundingBox bounding_box) { // Vérification de la cohérence entre les résolutions et bbox fournies et les dimensions (en pixel) de l'image // Arrondi a la valeur entiere la plus proche - if (resolution_x == 0 || resolution_y == 0) return false; + if (resolution_x == 0 || resolution_y == 0) + return false; - int calcWidth = lround ( ( bounding_box.xmax - bounding_box.xmin ) / ( resolution_x ) ); - int calcHeight = lround ( ( bounding_box.ymax - bounding_box.ymin ) / ( resolution_y ) ); + int calcWidth = lround((bounding_box.xmax - bounding_box.xmin) / (resolution_x)); + int calcHeight = lround((bounding_box.ymax - bounding_box.ymin) / (resolution_y)); - if ( calcWidth != w || calcHeight != h ) { - BOOST_LOG_TRIVIAL(warning) << "height is " << h << " and calculation give " << calcHeight ; - BOOST_LOG_TRIVIAL(warning) << "width is " << w << " and calculation give " << calcWidth ; + if (calcWidth != w || calcHeight != h) { + BOOST_LOG_TRIVIAL(warning) << "height is " << h << " and calculation give " << calcHeight; + BOOST_LOG_TRIVIAL(warning) << "width is " << w << " and calculation give " << calcWidth; return false; } - + return true; } @@ -179,9 +178,7 @@ class Image { * \~english * \brief Define the image as a mask */ - inline void make_mask () { - is_mask = true; - } + inline void make_mask() { is_mask = true; } /** * \~french @@ -191,9 +188,7 @@ class Image { * \brief Return the number of samples per pixel * \return channels */ - virtual int get_channels() { - return channels; - } + virtual int get_channels() { return channels; } /** * \~french @@ -203,9 +198,7 @@ class Image { * \brief Return the image's width * \return width */ - int inline get_width() { - return width; - } + int inline get_width() { return width; } /** * \~french @@ -215,9 +208,7 @@ class Image { * \brief Return the image's height * \return height */ - int inline get_height() { - return height; - } + int inline get_height() { return height; } /** * \~french @@ -227,12 +218,15 @@ class Image { * \brief Define the image's bounding box and calculate resolutions * \param[in] box Image's bounding box */ - inline void set_bbox ( BoundingBox box ) { + inline void set_bbox(BoundingBox box) { bbox = box; if (crs != NULL) { bbox.crs = crs->get_request_code(); } compute_resolutions(); + if (!is_mask && mask != NULL) { + mask->set_bbox(box); + } } /** @@ -243,12 +237,13 @@ class Image { * \brief Define the image's bounding box and calculate resolutions * \param[in] box Image's bounding box */ - inline bool set_dimensions ( int w, int h, BoundingBox box, double rx, double ry ) { + inline bool set_dimensions(int w, int h, BoundingBox box, double rx, double ry) { double calcWidth = (box.xmax - box.xmin) / rx; double calcHeight = (box.ymax - box.ymin) / ry; - - if ( std::abs(calcWidth - w) > 10E-3 || std::abs(calcHeight - h) > 10E-3) return false; - + + if (std::abs(calcWidth - w) > 10E-3 || std::abs(calcHeight - h) > 10E-3) + return false; + width = w; height = h; resx = rx; @@ -266,9 +261,7 @@ class Image { * \brief Return the image's bounding box * \return bounding box */ - BoundingBox inline get_bbox() const { - return bbox; - } + BoundingBox inline get_bbox() const { return bbox; } /** * \~french @@ -279,7 +272,7 @@ class Image { * \brief Define the CRS of the image's bounding box * \param[in] box Image's bounding box */ - void set_crs ( CRS* c ) { + void set_crs(CRS* c) { crs = NULL; if (c != NULL) { @@ -288,6 +281,10 @@ class Image { } else { bbox.crs = ""; } + + if (!is_mask && mask != NULL) { + mask->set_crs(c); + } } /** @@ -298,9 +295,7 @@ class Image { * \brief Return the image's bounding box's CRS * \return CRS */ - CRS* get_crs() const { - return crs; - } + CRS* get_crs() const { return crs; } /** * \~french @@ -310,9 +305,7 @@ class Image { * \brief Return bounding box's xmin * \return xmin */ - double inline get_xmin() const { - return bbox.xmin; - } + double inline get_xmin() const { return bbox.xmin; } /** * \~french * \brief Retourne le ymax de l'emprise rectangulaire @@ -321,9 +314,7 @@ class Image { * \brief Return bounding box's ymax * \return ymax */ - double inline get_ymax() const { - return bbox.ymax; - } + double inline get_ymax() const { return bbox.ymax; } /** * \~french * \brief Retourne le xmax de l'emprise rectangulaire @@ -332,9 +323,7 @@ class Image { * \brief Return bounding box's xmax * \return xmax */ - double inline get_xmax() const { - return bbox.xmax; - } + double inline get_xmax() const { return bbox.xmax; } /** * \~french * \brief Retourne le ymin de l'emprise rectangulaire @@ -343,9 +332,7 @@ class Image { * \brief Return bounding box's ymin * \return ymin */ - double inline get_ymin() const { - return bbox.ymin; - } + double inline get_ymin() const { return bbox.ymin; } /** * \~french @@ -358,7 +345,8 @@ class Image { * \return X resolution */ inline double get_resx(bool force_meter = false) const { - if (force_meter && crs->get_meters_per_unit() != 1.0) return resx * METER_PER_DEG; + if (force_meter && crs->get_meters_per_unit() != 1.0) + return resx * METER_PER_DEG; return resx; } /** @@ -372,7 +360,8 @@ class Image { * \return Y resolution */ inline double get_resy(bool force_meter = false) const { - if (force_meter && crs->get_meters_per_unit() != 1.0) return resy * METER_PER_DEG; + if (force_meter && crs->get_meters_per_unit() != 1.0) + return resy * METER_PER_DEG; return resy; } @@ -384,9 +373,7 @@ class Image { * \brief Return the associated mask * \return mask */ - inline Image* get_mask() { - return mask; - } + inline Image* get_mask() { return mask; } /** * \~french @@ -396,17 +383,17 @@ class Image { * \brief Defined data mask and check consistency * \param[in] new_mask Masque de donnée */ - inline bool set_mask ( Image* new_mask ) { + inline bool set_mask(Image* new_mask) { if (mask != NULL) { // On a déjà un masque associé : on le supprime pour le remplacer par le nouveau delete mask; } - - if ( new_mask->get_width() != width || new_mask->get_height() != height || new_mask->get_channels() != 1 ) { - BOOST_LOG_TRIVIAL(error) << "Unvalid mask" ; - BOOST_LOG_TRIVIAL(error) << "\t - channels have to be 1, it is " << new_mask->get_channels() ; - BOOST_LOG_TRIVIAL(error) << "\t - width have to be " << width << ", it is " << new_mask->get_width() ; - BOOST_LOG_TRIVIAL(error) << "\t - height have to be " << height << ", it is " << new_mask->get_height() ; + + if (new_mask->get_width() != width || new_mask->get_height() != height || new_mask->get_channels() != 1) { + BOOST_LOG_TRIVIAL(error) << "Unvalid mask"; + BOOST_LOG_TRIVIAL(error) << "\t - channels have to be 1, it is " << new_mask->get_channels(); + BOOST_LOG_TRIVIAL(error) << "\t - width have to be " << width << ", it is " << new_mask->get_width(); + BOOST_LOG_TRIVIAL(error) << "\t - height have to be " << height << ", it is " << new_mask->get_height(); return false; } @@ -426,9 +413,7 @@ class Image { * \param[in] x terrain coordinate X * \return column */ - int inline x_to_column ( double x ) { - return floor ( ( x-bbox.xmin ) /resx ); - } + int inline x_to_column(double x) { return floor((x - bbox.xmin) / resx); } /** * \~french * \brief Conversion de l'ordonnée terrain vers l'indice de ligne dans l'image @@ -439,9 +424,7 @@ class Image { * \param[in] y terrain coordinate Y * \return line */ - int inline y_to_line ( double y ) { - return floor ( ( bbox.ymax-y ) /resy ); - } + int inline y_to_line(double y) { return floor((bbox.ymax - y) / resy); } /** * \~french @@ -453,9 +436,7 @@ class Image { * \param[in] c column * \return terrain coordinate X */ - double inline column_to_x ( int c ) { - return ( bbox.xmin + (0.5 + c) * resx ); - } + double inline column_to_x(int c) { return (bbox.xmin + (0.5 + c) * resx); } /** * \~french * \brief Conversion de l'indice de ligne dans l'image vers l'ordonnée terrain du centre du pixel @@ -466,9 +447,7 @@ class Image { * \param[in] l line * \return terrain coordinate Y */ - double inline line_to_y ( int l ) { - return ( bbox.ymax - (0.5 + l) * resy ); - } + double inline line_to_y(int l) { return (bbox.ymax - (0.5 + l) * resy); } /** * \~french @@ -481,9 +460,7 @@ class Image { * \image html phases.png * \return X phasis */ - double inline get_phasex() { - return bbox.get_xmin_phase(resx); - } + double inline get_phasex() { return bbox.get_xmin_phase(resx); } /** * \~french @@ -494,9 +471,7 @@ class Image { * \brief Phasis calculation, Y wise * \return Y phasis */ - double inline get_phasey() { - return bbox.get_ymin_phase(resy); - } + double inline get_phasey() { return bbox.get_ymin_phase(resy); } /** * \~french @@ -520,36 +495,37 @@ class Image { * \param[in] other image to compare * \return compatibility */ - bool compatible ( Image* other ) { + bool compatible(Image* other) { - if ( crs != NULL && crs->is_define() && other->get_crs() != NULL && other->get_crs()->is_define() && ! crs->cmp_request_code(other->get_crs()->get_request_code()) ) { - BOOST_LOG_TRIVIAL(debug) << "Different CRS" ; + if (crs != NULL && crs->is_define() && other->get_crs() != NULL && other->get_crs()->is_define() && + !crs->cmp_request_code(other->get_crs()->get_request_code())) { + BOOST_LOG_TRIVIAL(debug) << "Different CRS"; return false; } - if ( get_channels() != other->get_channels() ) { - BOOST_LOG_TRIVIAL(debug) << "Different channels" ; + if (get_channels() != other->get_channels()) { + BOOST_LOG_TRIVIAL(debug) << "Different channels"; return false; } - double epsilon_x=std::min ( get_resx(), other->get_resx() ) /1000.; - double epsilon_y=std::min ( get_resy(), other->get_resy() ) /1000.; + double epsilon_x = std::min(get_resx(), other->get_resx()) / 1000.; + double epsilon_y = std::min(get_resy(), other->get_resy()) / 1000.; - if ( fabs ( get_resx()-other->get_resx() ) > epsilon_x ) { - BOOST_LOG_TRIVIAL(debug) << "Different X resolutions" ; + if (fabs(get_resx() - other->get_resx()) > epsilon_x) { + BOOST_LOG_TRIVIAL(debug) << "Different X resolutions"; return false; } - if ( fabs ( get_resy()-other->get_resy() ) > epsilon_y ) { - BOOST_LOG_TRIVIAL(debug) << "Different Y resolutions" ; + if (fabs(get_resy() - other->get_resy()) > epsilon_y) { + BOOST_LOG_TRIVIAL(debug) << "Different Y resolutions"; return false; } - if ( fabs ( get_phasex()-other->get_phasex() ) > 0.001 && fabs ( get_phasex()-other->get_phasex() ) < 0.999 ) { - BOOST_LOG_TRIVIAL(debug) << "Different X phasis : " << get_phasex() << " and " << other->get_phasex() ; + if (fabs(get_phasex() - other->get_phasex()) > 0.001 && fabs(get_phasex() - other->get_phasex()) < 0.999) { + BOOST_LOG_TRIVIAL(debug) << "Different X phasis : " << get_phasex() << " and " << other->get_phasex(); return false; } - if ( fabs ( get_phasey()-other->get_phasey() ) > 0.001 && fabs ( get_phasey()-other->get_phasey() ) < 0.999 ) { - BOOST_LOG_TRIVIAL(debug) << "Different Y phasis : " << get_phasey() << " and " << other->get_phasey() ; + if (fabs(get_phasey() - other->get_phasey()) > 0.001 && fabs(get_phasey() - other->get_phasey()) < 0.999) { + BOOST_LOG_TRIVIAL(debug) << "Different Y phasis : " << get_phasey() << " and " << other->get_phasey(); return false; } @@ -574,9 +550,16 @@ class Image { * \param[in] resy Y wise resolution * \param[in] bbox bounding box */ - Image ( int width, int height, int channels, double resx, double resy, BoundingBox bbox ) : - width ( width ), height ( height ), channels ( channels ), resx ( resx ), resy ( resy ), bbox ( bbox ), crs ( NULL ), mask ( NULL ), is_mask(false) - { + Image(int width, int height, int channels, double resx, double resy, BoundingBox bbox) + : width(width), + height(height), + channels(channels), + resx(resx), + resy(resy), + bbox(bbox), + crs(NULL), + mask(NULL), + is_mask(false) { are_dimensions_consistent(resx, resy, width, height, bbox); } @@ -593,7 +576,16 @@ class Image { * \param[in] height image height, in pixel * \param[in] channel number of samples per pixel */ - Image ( int width, int height, int channels ) : width ( width ), height ( height ), channels ( channels ), resx ( 1. ), resy ( 1. ), bbox ( BoundingBox ( 0., 0., ( double ) width, ( double ) height ) ), crs ( NULL ), mask ( NULL ), is_mask ( false ) {} + Image(int width, int height, int channels) + : width(width), + height(height), + channels(channels), + resx(1.), + resy(1.), + bbox(BoundingBox(0., 0., (double)width, (double)height)), + crs(NULL), + mask(NULL), + is_mask(false) {} /** * \~french @@ -610,9 +602,8 @@ class Image { * \param[in] channel number of samples per pixel * \param[in] bbox bounding box */ - Image ( int width, int height, int channels, BoundingBox bbox ) : - width ( width ), height ( height ), channels ( channels ), bbox ( bbox ), crs ( NULL ), mask ( NULL ), is_mask ( false ) - { + Image(int width, int height, int channels, BoundingBox bbox) + : width(width), height(height), channels(channels), bbox(bbox), crs(NULL), mask(NULL), is_mask(false) { compute_resolutions(); } @@ -632,10 +623,16 @@ class Image { * \param[in] resx X wise resolution * \param[in] resy Y wise resolution */ - Image ( int width, int height, int channels, double resx, double resy ) : - width ( width ), height ( height ), channels ( channels ), resx ( resx ), resy ( resy ), - bbox ( BoundingBox ( 0., 0., resx * ( double ) width, resy * ( double ) height ) ), - mask ( NULL ), is_mask ( false ), crs ( NULL ) {} + Image(int width, int height, int channels, double resx, double resy) + : width(width), + height(height), + channels(channels), + resx(resx), + resy(resy), + bbox(BoundingBox(0., 0., resx * (double)width, resy * (double)height)), + mask(NULL), + is_mask(false), + crs(NULL) {} /** * \~french @@ -645,8 +642,8 @@ class Image { * \param[in] line Indice de la ligne à retourner (0 <= line < height) * \return taille utile du buffer, 0 si erreur */ - virtual int get_line ( uint8_t *buffer, int line ) = 0; - + virtual int get_line(uint8_t* buffer, int line) = 0; + /** * \~french * \brief Retourne une ligne en entier 16 bits. @@ -655,7 +652,7 @@ class Image { * \param[in] line Indice de la ligne à retourner (0 <= line < height) * \return taille utile du buffer, 0 si erreur */ - virtual int get_line ( uint16_t *buffer, int line ) = 0; + virtual int get_line(uint16_t* buffer, int line) = 0; /** * \~french @@ -665,7 +662,7 @@ class Image { * \param[in] line Indice de la ligne à retourner (0 <= line < height) * \return taille utile du buffer, 0 si erreur */ - virtual int get_line ( float *buffer, int line ) = 0; + virtual int get_line(float* buffer, int line) = 0; /** * \~french @@ -674,7 +671,8 @@ class Image { * \brief Default destructor */ virtual ~Image() { - if ( mask != NULL ) delete mask; + if (mask != NULL) + delete mask; } /** @@ -684,27 +682,27 @@ class Image { * \brief Image description output */ virtual void print() { - BOOST_LOG_TRIVIAL(info) << "\t- width = " << width << ", height = " << height ; - BOOST_LOG_TRIVIAL(info) << "\t- samples per pixel = " << channels ; + BOOST_LOG_TRIVIAL(info) << "\t- width = " << width << ", height = " << height; + BOOST_LOG_TRIVIAL(info) << "\t- samples per pixel = " << channels; if (crs != NULL) { - BOOST_LOG_TRIVIAL(info) << "\t- CRS = " << crs->get_proj_code() ; + BOOST_LOG_TRIVIAL(info) << "\t- CRS = " << crs->get_proj_code(); } else { - BOOST_LOG_TRIVIAL(info) << "\t- No CRS" ; + BOOST_LOG_TRIVIAL(info) << "\t- No CRS"; } - BOOST_LOG_TRIVIAL(info) << "\t- bbox = " << bbox.to_string() ; - BOOST_LOG_TRIVIAL(info) << "\t- x resolution = " << resx << ", y resolution = " << resy ; - if ( is_mask ) { - BOOST_LOG_TRIVIAL(info) << "\t- Is a mask" ; + BOOST_LOG_TRIVIAL(info) << "\t- bbox = " << bbox.to_string(); + BOOST_LOG_TRIVIAL(info) << "\t- x resolution = " << resx << ", y resolution = " << resy; + if (is_mask) { + BOOST_LOG_TRIVIAL(info) << "\t- Is a mask"; } else { - BOOST_LOG_TRIVIAL(info) << "\t- Is not a mask" ; + BOOST_LOG_TRIVIAL(info) << "\t- Is not a mask"; } - if ( mask ) { - BOOST_LOG_TRIVIAL(info) << "\t- Own a mask\n" ; + if (mask) { + BOOST_LOG_TRIVIAL(info) << "\t- Own a mask\n"; } else { - BOOST_LOG_TRIVIAL(info) << "\t- No mask\n" ; + BOOST_LOG_TRIVIAL(info) << "\t- No mask\n"; } } - + /** * \~french * \brief Sortie du tfw de l'image @@ -712,7 +710,10 @@ class Image { * \brief Image TFW output */ virtual void print_tfw() { - BOOST_LOG_TRIVIAL(info) << "TFW : \n" << resx << "\n-" << resy << "\n0\n0\n" << bbox.xmin+0.5*resx << "\n" << bbox.ymax - 0.5*resy ; + BOOST_LOG_TRIVIAL(info) << "TFW : \n" + << resx << "\n-" << resy << "\n0\n0\n" + << bbox.xmin + 0.5 * resx << "\n" + << bbox.ymax - 0.5 * resy; } /** @@ -723,12 +724,9 @@ class Image { */ virtual float get_mean_resolution() { if (crs->get_meters_per_unit() != 1.0) { - return ((resx+resy)/2.0)*METER_PER_DEG; + return ((resx + resy) / 2.0) * METER_PER_DEG; } else { - return (resx+resy)/2.0; + return (resx + resy) / 2.0; } - } }; - - diff --git a/include/rok4/image/SubsampledImage.h b/include/rok4/image/SubsampledImage.h new file mode 100644 index 0000000..2d9a376 --- /dev/null +++ b/include/rok4/image/SubsampledImage.h @@ -0,0 +1,173 @@ +/* + * Copyright © (2011) Institut national de l'information + * géographique et forestière + * + * Géoportail SAV + * + * This software is a computer program whose purpose is to publish geographic + * data using OGC WMS and WMTS protocol. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * + * knowledge of the CeCILL-C license and that you accept its terms. + */ + +/** + * \file SubsampledImage.h + ** \~french + * \brief Définition des classes SubsampledImage + * \details + * \li SubsampledImage : image résultant du sous échantillonnage d'une image source + ** \~english + * \brief Define classes SubsampledImage + * \details + * \li SubsampledImage : image built by source image's sub sampling + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include "rok4/utils/Utils.h" +#include "rok4/image/Image.h" + +/** + * \author Institut national de l'information géographique et forestière + * \~french + * \brief Décimation d'une image, c'est à dire qu'on ne garde qu'un seul pixel tous les N pixels + */ +class SubsampledImage : public Image { + +private: + + /** + * \~french \brief Image source à décimer + * \~english \brief Source image, to decimate + */ + Image* source_image; + + /** + * \~french \brief Facteur de sous échantillonnage dans le sens des X + * \~english \brief Subsampling's factor, widthwise + */ + int ratio_x; + /** + * \~french \brief Facteur de sous échantillonnage dans le sens des Y + * \~english \brief Subsampling's factor, heightwise + */ + int ratio_y; + + /** \~french + * \brief Retourne une ligne, flottante ou entière + * \details Lorsque l'on veut récupérer une ligne d'une image décimée, on ne garde que un pixel tous les #ratio_x de l'image source + * + * \param[in] buffer Tableau contenant au moins width*channels valeurs + * \param[in] line Indice de la ligne à retourner (0 <= line < height) + * \return taille utile du buffer, 0 si erreur + */ + template + int _getline ( T* buffer, int line ); + + /** \~french + * \brief Crée un objet SubsampledImage à partir de tous ses éléments constitutifs + * \details Ce constructeur est privé afin de n'être appelé que par la méthode statique #create, qui fera différents tests et calculs. + * \param[in] width largeur de l'image en pixel + * \param[in] height hauteur de l'image en pixel + * \param[in] channel nombre de canaux par pixel + * \param[in] resx résolution dans le sens des X + * \param[in] resy résolution dans le sens des Y + * \param[in] bbox emprise rectangulaire de l'image + * \param[in] image image source + * \param[in] nd valeur de non-donnée + ** \~english + * \brief Create an SubsampledImage object, from all attributes + * \param[in] width image width, in pixel + * \param[in] height image height, in pixel + * \param[in] channel number of samples per pixel + * \param[in] resx X wise resolution + * \param[in] resy Y wise resolution + * \param[in] bbox bounding box + * \param[in] image source image + * \param[in] nd nodata value + */ + SubsampledImage ( Image* image, int ratio_x, int ratio_y); + +public: + + int get_line ( uint8_t* buffer, int line ); + int get_line ( uint16_t* buffer, int line ); + int get_line ( float* buffer, int line ); + + /** + * \~french + * \brief Destructeur par défaut + * \details Suppression de l'image source de la SubsampledImage + * \~english + * \brief Default destructor + */ + virtual ~SubsampledImage() { + if (! is_mask) { + delete source_image; + } + } + + /** \~french + * \brief Sortie des informations sur l'image composée + ** \~english + * \brief Compounded image description output + */ + void print() { + BOOST_LOG_TRIVIAL(info) << "" ; + BOOST_LOG_TRIVIAL(info) << "------ SubsampledImage -------" ; + Image::print(); + BOOST_LOG_TRIVIAL(info) << "\t- X wise ratio : " << ratio_x; + BOOST_LOG_TRIVIAL(info) << "\t- Y wise ratio : " << ratio_y; + BOOST_LOG_TRIVIAL(info) << "" ; + } + + /** \~french + * \brief Teste et calcule les caractéristiques de l'image sous échantillonnée et crée un objet SubsampledImage + * \details Largeur, hauteur, nombre de canaux et bbox sont déduits des composantes de l'image source et des paramètres. On vérifie que les dimensions sont divisibles par les facteurs. + * \param[in] image image source + * \param[in] ratio_x facteur de sous échantillonnage en X + * \param[in] ratio_y facteur de sous échantillonnage en Y + * \return un pointeur d'objet SubsampledImage, NULL en cas d'erreur + ** \~english + * \brief Check and calculate compounded image components and create an SubsampledImage object + * \details Height, width, samples' number and bbox are deduced from source image's components and parameters. We check if dimensions are multiple of factors. + * \param[in] image source image + * \param[in] ratio_x Widthwise subsampling factor + * \param[in] ratio_y Heightwise subsampling factor + * \return a SubsampledImage object pointer, NULL if error + */ + static SubsampledImage* create ( Image* image, int ratio_x, int ratio_y ); +}; + + diff --git a/src/image/SubsampledImage.cpp b/src/image/SubsampledImage.cpp new file mode 100644 index 0000000..04f5949 --- /dev/null +++ b/src/image/SubsampledImage.cpp @@ -0,0 +1,171 @@ +/* + * Copyright © (2011) Institut national de l'information + * géographique et forestière + * + * Géoportail SAV + * + * This software is a computer program whose purpose is to publish geographic + * data using OGC WMS and WMTS protocol. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * + * knowledge of the CeCILL-C license and that you accept its terms. + */ + +/** + * \file SubsampledImage.cpp + ** \~french + * \brief Implémentation des classes SubsampledImage, DecimatedMask + * \details + * \li SubsampledImage : image calculée à d'images compatibles, superposables + * \li DecimatedMask : masque composé, associé à une image composée + ** \~english + * \brief Implement classes SubsampledImage, DecimatedMask + * \details + * \li SubsampledImage : image compounded with superimpose images + * \li DecimatedMask : compounded mask, associated with a compounded image + */ + +#include "image/SubsampledImage.h" +#include +#include "utils/Utils.h" + + +/********************************************** SubsampledImage ************************************************/ + +template +int SubsampledImage::_getline ( T* buffer, int line ) { + + // On a besoin de récupérer ratio_y lignes dans l'image source + T* source_image_lines = new T[ratio_y * source_image->get_width() * source_image->get_channels()]; + uint8_t* source_mask_lines = NULL; + + for ( int i = 0; i < ratio_y; i++ ) { + if (source_image->get_line ( source_image_lines + i * source_image->get_width() * source_image->get_channels(), line * ratio_y + i ) == 0) { + BOOST_LOG_TRIVIAL(error) << "Cannot read line " << line * ratio_y + i << "from source to process SubsampledImage's line " << line ; + return 0; + } + } + + // On récupère les lignes de masques si il est présent + bool mask = false; + if (source_image->get_mask() != NULL) { + mask = true; + source_mask_lines = new uint8_t[ratio_y * source_image->get_width()]; + for ( int i = 0; i < ratio_y; i++ ) { + if (source_image->get_mask()->get_line ( source_mask_lines + i * source_image->get_width(), line * ratio_y + i ) == 0) { + BOOST_LOG_TRIVIAL(error) << "Cannot read mask line " << line * ratio_y + i << "from source to process SubsampledImage's line " << line ; + return 0; + } + } + } + + for (int pixel = 0; pixel < width; pixel++) { + + int data_count; + + for (int band = 0; band < channels; band++) { + data_count = 0; + float value = 0; + + for (int x = 0; x < ratio_x; x++) { + for (int y = 0; y < ratio_y; y++) { + + if (mask && source_mask_lines[source_image->get_width() * y + pixel * ratio_x + x] == 0) { + // ce n'est pas un pixel de donnée, on passe + continue; + } + + data_count++; + value += source_image_lines[source_image->get_width() * channels * y + pixel * ratio_x * channels + x * channels + band]; + } + } + + buffer[pixel * channels + band] = (T) (value / data_count); + } + + if (data_count == 0) { + // On avait aucun pixel de donnée, on va mettre la valeur du premier pixel de la zone, considéré comme le nodata de l'image en entrée + for (int band = 0; band < channels; band++) { + buffer[pixel * channels + band] = source_image_lines[pixel * ratio_x * channels + band]; + } + } + } + + delete [] source_image_lines; + if (source_mask_lines != 0) { + delete [] source_mask_lines; + } + + return width * channels; +} + + +/* Implementation de get_line pour les uint8_t */ +int SubsampledImage::get_line ( uint8_t* buffer, int line ) { + return _getline ( buffer, line ); +} + +/* Implementation de get_line pour les float */ +int SubsampledImage::get_line ( uint16_t* buffer, int line ) { + return _getline ( buffer, line ); +} + +/* Implementation de get_line pour les float */ +int SubsampledImage::get_line ( float* buffer, int line ) { + return _getline ( buffer, line ); +} + +SubsampledImage::SubsampledImage ( Image* image, int ratio_x, int ratio_y ) : + Image ( image->get_width() / ratio_x, image->get_height() / ratio_y, image->get_channels(), image->get_bbox() ), + source_image ( image ), ratio_x (ratio_x), ratio_y (ratio_y) { + +} + +SubsampledImage* SubsampledImage::create ( Image* image, int ratio_x, int ratio_y ) { + + if ( image == NULL ) { + BOOST_LOG_TRIVIAL(error) << "No source image to define subsampled image" ; + return NULL; + } + + // On vérifie que les facteur de sous échantillonnage + + if (image->get_width() % ratio_x != 0) { + BOOST_LOG_TRIVIAL(error) << "Width have to be a multiple of widthwise subsampling factor" ; + return NULL; + } + + if (image->get_height() % ratio_y != 0) { + BOOST_LOG_TRIVIAL(error) << "Height have to be a multiple of heightwise subsampling factor" ; + return NULL; + } + + SubsampledImage* sub = new SubsampledImage ( image, ratio_x, ratio_y); + + return sub; +} +