/* Copyright (c) 2008-2022 Jan W. Krieger () This software is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation, either version 2.1 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 Lesser General Public License (LGPL) for more details. You should have received a copy of the GNU Lesser General Public License (LGPL) along with this program. If not, see . */ #ifndef JKQTPBASICIMAGETOOLS_H #define JKQTPBASICIMAGETOOLS_H #include #include #include #include #include #include #include #include #include #include #include "jkqtcommon/jkqtcommon_imexport.h" #include "jkqtcommon/jkqtpmathtools.h" /** \brief possible datatypes of the data array, plotted by this class. \ingroup jkqtplotter_imagelots_tools */ enum class JKQTPMathImageDataType { FloatArray, /*!< Data is of type \c float */ DoubleArray, /*!< Data is of type \c double */ UInt8Array, /*!< Data is of type \c uint8_t */ UInt16Array, /*!< Data is of type \c uint16_t */ UInt32Array, /*!< Data is of type \c uint32_t */ UInt64Array, /*!< Data is of type \c uint8_t */ Int8Array, /*!< Data is of type \c int8_t */ Int16Array, /*!< Data is of type \c int16_t */ Int32Array, /*!< Data is of type \c int32_t */ Int64Array /*!< Data is of type \c int64_t */ }; /*! \brief retrieve an R/G/B/Alpha (\a ch == 0/1/2/3) value from the \c QRgb value \a rgb \ingroup jkqtplotter_imagelots_tools */ inline int JKQTPGetColorChannel(QRgb rgb, int ch) { switch(ch) { case 0: return qRed(rgb); case 1: return qGreen(rgb); case 2: return qBlue(rgb); case 3: return qAlpha(rgb); } return qGray(rgb); } /*! \brief set the R/G/B/Alpha (\a ch == 0/1/2/3) value in the \c QRgb value \a rgb to \a val (0..255!) \ingroup jkqtplotter_imagelots_tools */ inline void JKQTPSetColorChannel(QRgb& rgb, int ch, int val) { switch(ch) { case 0: rgb= qRgba(val, qGreen(rgb), qBlue(rgb), qAlpha(rgb)); break; case 1: rgb= qRgba(qRed(rgb), val, qBlue(rgb), qAlpha(rgb)); break; case 2: rgb= qRgba(qRed(rgb), qGreen(rgb), val, qAlpha(rgb)); break; case 3: rgb= qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), val); break; } } /*! \brief available palettes for coloring an image \ingroup jkqtplotter_imagelots_tools */ enum JKQTPMathImageColorPalette { JKQTPMathImageGRAY=0, /*!< \image html palettes/palette_gray.png */ JKQTPMathImageINVERTEDGRAY, /*!< \image html palettes/palette_invgray.png */ JKQTPMathImageRED, /*!< \image html palettes/palette_red.png */ JKQTPMathImageINVERTEDRED, /*!< \image html palettes/palette_invred.png */ JKQTPMathImageGREEN, /*!< \image html palettes/palette_green.png */ JKQTPMathImageINVERTEDGREEN, /*!< \image html palettes/palette_invgreen.png */ JKQTPMathImageBLUE, /*!< \image html palettes/palette_blue.png */ JKQTPMathImageINVERTEDBLUE, /*!< \image html palettes/palette_invblue.png */ JKQTPMathImageCYAN, /*!< \image html palettes/palette_cyan.png */ JKQTPMathImageINVERTED_CYAN, /*!< \image html palettes/palette_invcyan.png */ JKQTPMathImageYELLOW, /*!< \image html palettes/palette_yellow.png */ JKQTPMathImageINVERTED_YELLOW, /*!< \image html palettes/palette_invyellow.png */ JKQTPMathImageMAGENTA, /*!< \image html palettes/palette_magenta.png */ JKQTPMathImageINVERTED_MAGENTA, /*!< \image html palettes/palette_invmagenta.png */ JKQTPMathImageMATLAB, /*!< \image html palettes/palette_Matlab.png */ JKQTPMathImageINVERTED_MATLAB, /*!< \image html palettes/palette_invMatlab.png */ JKQTPMathImageRYGB, /*!< \image html palettes/palette_RYGB.png */ JKQTPMathImageINVERTED_RYGB, /*!< \image html palettes/palette_invRYGB.png */ JKQTPMathImageHSV, /*!< \image html palettes/palette_HSV.png */ JKQTPMathImageINVERTED_HSV, /*!< \image html palettes/palette_invHSV.png */ JKQTPMathImageRAINBOW, /*!< \image html palettes/palette_rainbow.png */ JKQTPMathImageINVERTED_RAINBOW, /*!< \image html palettes/palette_invrainbow.png */ JKQTPMathImageHOT, /*!< \image html palettes/palette_AFMhot.png */ JKQTPMathImageINVERTED_HOT, /*!< \image html palettes/palette_invAFMhot.png */ JKQTPMathImageOCEAN, /*!< \image html palettes/palette_ocean.png */ JKQTPMathImageINVERTED_OCEAN, /*!< \image html palettes/palette_invocean.png */ JKQTPMathImageTRAFFICLIGHT, /*!< \image html palettes/palette_trafficlight.png */ JKQTPMathImageINVERTED_TRAFFICLIGHT, /*!< \image html palettes/palette_invtrafficlight.png */ JKQTPMathImageBone, /*!< \image html palettes/palette_bone.png */ JKQTPMathImageCool, /*!< \image html palettes/palette_cool.png */ JKQTPMathImageCopper, /*!< \image html palettes/palette_copper.png */ JKQTPMathImageAutumn, /*!< \image html palettes/palette_autumn.png */ JKQTPMathImageSeismic, /*!< \image html palettes/palette_seismic.png */ JKQTPMathImageTerrain, /*!< \image html palettes/palette_terrain.png */ JKQTPMathImageViridis, /*!< \image html palettes/palette_viridis.png \see from https://github.com/BIDS/colormap/blob/master/colormaps.py https://github.com/BIDS/colormap/blob/master/colormaps.py */ JKQTPMathImageMagma, /*!< \image html palettes/palette_magma.png \see from https://github.com/BIDS/colormap/blob/master/colormaps.py https://github.com/BIDS/colormap/blob/master/colormaps.py */ JKQTPMathImageInferno, /*!< \image html palettes/palette_inferno.png \see from https://github.com/BIDS/colormap/blob/master/colormaps.py https://github.com/BIDS/colormap/blob/master/colormaps.py */ JKQTPMathImagePlasma, /*!< \image html palettes/palette_plasma.png \see from https://github.com/BIDS/colormap/blob/master/colormaps.py https://github.com/BIDS/colormap/blob/master/colormaps.py */ JKQTPMathImageBLUEMAGENTAYELLOW, /*!< \image html palettes/palette_BlMaYe.png */ JKQTPMathImageINVERTED_BLUEMAGENTAYELLOW, /*!< \image html palettes/palette_YeMaBl.png */ JKQTPMathImageYELLOWMAGENTABLUE=JKQTPMathImageINVERTED_BLUEMAGENTAYELLOW, /*!< \image html palettes/palette_YeMaBl.png */ JKQTPMathImageBLUEYELLOW, /*!< \image html palettes/palette_BlYe.png */ JKQTPMathImageINVERTED_BLUEYELLOW, /*!< \image html palettes/palette_YeBl.png */ JKQTPMathImageYELLOWBLUE=JKQTPMathImageINVERTED_BLUEYELLOW, /*!< \image html palettes/palette_YeBl.png */ JKQTPMathImageBLUEWHITERED, /*!< \image html palettes/palette_bluewhitered.png */ JKQTPMathImageREDWHITEBLUE, /*!< \image html palettes/palette_redwhiteblue.png */ JKQTPMathImageBLACKBLUEREDYELLOW, /*!< \image html palettes/palette_BBlRdYe.png */ JKQTPMathImageGREENREDVIOLET, /*!< \image html palettes/palette_GnRdVi.png */ JKQTPMathImageBLACKBLUEWHITEYELLOWWHITE, /*!< \image html palettes/palette_BWprint.png */ JKQTPMathImageWHITEYELLOWWHITEBLUEBLACK, /*!< \image html palettes/palette_invBWprint.png */ JKQTPMathImageBR_GR, /*!< \image html palettes/palette_BrBG.png */ JKQTPMathImageBrownGreen=JKQTPMathImageBR_GR, /*!< \image html palettes/palette_BrBG.png */ JKQTPMathImagePU_OR, /*!< \image html palettes/palette_PuOr.png */ JKQTPMathImageOrangeWhitePurple=JKQTPMathImagePU_OR, /*!< \image html palettes/palette_PuOr.png */ JKQTPMathImageGN_BU, /*!< \image html palettes/palette_greenblue.png */ JKQTPMathImageGreenBlue=JKQTPMathImageGN_BU, /*!< \image html palettes/palette_greenblue.png */ JKQTPMathImageBU_GN, /*!< \image html palettes/palette_bluegreen.png */ JKQTPMathImageBlueGreen=JKQTPMathImageBU_GN, /*!< \image html palettes/palette_bluegreen.png */ JKQTPMathImageYL_GN_BU, /*!< \image html palettes/palette_YeGnBu.png */ JKQTPMathImageYellowGreenBlue=JKQTPMathImageYL_GN_BU, /*!< \image html palettes/palette_YeGnBu.png */ JKQTPMathImageBR_GR_STEP, /*!< \image html palettes/palette_stepsBrBG.png */ JKQTPMathImagePU_OR_STEP, /*!< \image html palettes/palette_stepsPuOr.png */ JKQTPMathImageGN_BU_STEP, /*!< \image html palettes/palette_stepsGnBl.png */ JKQTPMathImageBU_GN_STEP, /*!< \image html palettes/palette_stepsBlGn.png */ JKQTPMathImageYL_GN_BU_STEP, /*!< \image html palettes/palette_stepsYeGnBu.png */ JKQTPMathImageCYANWHITE, /*!< \image html palettes/palette_cyanwhite.png */ JKQTPMathImageINVERTED_CYANWHITE, /*!< \image html palettes/palette_whitecyan.png */ JKQTPMathImageWHITECYAN=JKQTPMathImageINVERTED_CYANWHITE, /*!< \image html palettes/palette_whitecyan.png */ JKQTPMathImageYELLOWWHITE, /*!< \image html palettes/palette_yellowwhite.png */ JKQTPMathImageINVERTED_YELLOWWHITE, /*!< \image html palettes/palette_whiteyellow.png */ JKQTPMathImageWHITEYELLOW=JKQTPMathImageINVERTED_YELLOWWHITE, /*!< \image html palettes/palette_whiteyellow.png */ JKQTPMathImageMAGENTAWHITE, /*!< \image html palettes/palette_magentawhite.png */ JKQTPMathImageINVERTED_MAGENTAWHITE, /*!< \image html palettes/palette_whitemagenta.png */ JKQTPMathImageWHITEMAGENTA=JKQTPMathImageINVERTED_MAGENTAWHITE, /*!< \image html palettes/palette_whitemagenta.png */ JKQTPMathImageBlueGreenRed, /*!< \image html palettes/palette_bluegreenred.png */ JKQTPMathImageRedGreenBlue, /*!< \image html palettes/palette_redgreenblue.png */ JKQTPMathImageMagentaYellow, /*!< \image html palettes/palette_magentayellow.png */ JKQTPMathImageYellowMagenta, /*!< \image html palettes/palette_yellowmagenta.png */ JKQTPMathImageRedBlue, /*!< \image html palettes/palette_redblue.png */ JKQTPMathImageBlueRed, /*!< \image html palettes/palette_bluered.png */ JKQTPMathImagePREDEFINED_PALETTES_COUNT, /*!< \brief the number of predefined palettes */ JKQTPMathImageUSER_PALETTE=65000, /*!< \brief special value for JKQTPImageTools::array2image(), which signals the usage of a provided user-defined palette */ JKQTPMathImageALPHA=JKQTPMathImageUSER_PALETTE-2, /*!< \brief special palette with increasing alpha values */ JKQTPMathImageINVERTED_ALPHA=JKQTPMathImageUSER_PALETTE-1, /*!< \brief special palette with decreasing alpha values */ JKQTPMathImageFIRST_REGISTERED_USER_PALETTE=JKQTPMathImagePREDEFINED_PALETTES_COUNT, /*!< \brief the ID of the first user-defined paletted, registered with JKQTPImageTools::registerPalette() or JKQTPImageTools::registerPalettesFromFile() */ JKQTPMathImageLAST_POSSIBLE_REGISTERED_USER_PALETTE=JKQTPMathImageUSER_PALETTE-10, /*!< \brief the ID of the first user-defined paletted, registered with JKQTPImageTools::registerPalette() or JKQTPImageTools::registerPalettesFromFile() */ }; /*! \brief modes available for image pixels that are above/below the pixel value range \ingroup jkqtplotter_imagelots_tools */ enum JKQTPMathImageColorRangeFailAction { JKQTPMathImageLastPaletteColor=0, /*!< set to last color in the palette */ JKQTPMathImageGivenColor=1, /*!< set to the provided min/max color */ JKQTPMathImageTransparent=2 /*!< set transparent */ }; /*! \brief tool structure that summarizes several static properties of JKQTPlotters palette system, also provides functions to work with palettes and register user-defined palettes. \ingroup jkqtplotter_imagelots_tools \see \ref JKQTPlotterImagePlot */ struct JKQTPImageTools { /*! \brief Datatype to store lookup-tables used to map data values (scales to 0..size-1) onto RGB-colors */ typedef QVector LUTType; /*! \brief Width of the Palette-Icons, generated e.g. by JKQTPImageTools::GetPaletteIcon() */ static JKQTCOMMON_LIB_EXPORT const int PALETTE_ICON_WIDTH; /*! \brief Height of the Palette-Icons, generated e.g. by JKQTPImageTools::GetPaletteIcon() */ static JKQTCOMMON_LIB_EXPORT const int PALETTE_IMAGEICON_HEIGHT; /*! \brief size of the lookup tables used by JKQTFPimagePlot_array2image() */ static JKQTCOMMON_LIB_EXPORT const int LUTSIZE; /*! \brief loads all palettes defined in the given palette files \a filename into global_jkqtpimagetools_userluts and assigns a new LUT-ID >=JKQTPMathImageFIRST_REGISTERED_USER_PALETTE to each palette All new IDs are returned as a list The file might either be a palette XML-file or a CSV-file: \b CSV-files need to have one of the following two formats (RED/GREEN/BLUE: 0..255): \verbatim scalar, red, green, blue scalar, red, green, blue ... \endverbatim or simply \verbatim red, green, blue red, green, blue ... \endverbatim The parser will also allow tabs and whitespaces as column separators. \b CML-files need to have one of the following two formats (RED/GREEN/BLUE/OPACITY: 0..1): \verbatim ... \endverbatim optionally several \code ... \endcode definitions may be put below an arbitrarily named document node, e.g.: \verbatim ... ... ... \endverbatim \note In both cases, the palette will be formed without interpolation, i.e. using JKQTPBuildColorPaletteLUT() . If in addition \a interpolatePalette is set to \c true, the function JKQTPBuildColorPaletteLUTLinInterpolate() is used instead. \see \ref JKQTPlotterImagePlot , JKQTPBuildColorPaletteLUT() */ static JKQTCOMMON_LIB_EXPORT QVector registerPalettesFromFile(const QString& filename, bool interpolatePalette=false); /*! \brief registers a new LUT defined by \a paletteLut and with the given \a name (computer-readable) for later use, optionally stores also the human-readable and localized name \a nameT and assigns a new LUT-ID >=JKQTPMathImageFIRST_REGISTERED_USER_PALETTE to the palette and returns it \see \ref JKQTPlotterImagePlot, JKQTPBuildColorPaletteLUTLinInterpolate(), JKQTPBuildColorPaletteLUT(), JKQTPBuildColorPaletteLUTLinInterpolateSorted(), JKQTPBuildColorPaletteLUTSorted(), JKQTPBuildColorPaletteLUTLinInterpolate(), JKQTPBuildColorPaletteLUT() */ static JKQTCOMMON_LIB_EXPORT int registerPalette(const QString& name, const LUTType &paletteLut, const QString &nameT=QString()); /*! \brief convert a 2D image (as 1D array) into a QImage with given palette (see JKQTFPColorPalette) \param dbl_in pointer to a 1D array of template type \c T representing the image to plot. This array has to be of size \a width * \a height \param width width of the array in \a dbl \param height height of the array in \a dbl \param[out] img the QImage object to draw to (should be initialized as \c QImage::Format_ARGB32 ) \param palette the color palette to use for the display \param minColor lower boundary of color range in \a dbl pixels, if \a minColor == \a maxColor then this function will extract the image min and image max. \param maxColor upper boundary of color range in \a dbl pixels, if \a minColor == \a maxColor then this function will extract the image min and image max. \param paletteMinFail specifies what shell happen, when a value in \a dbl is below \a minColor \param paletteMaxFail specifies what shell happen, when a value in \a dbl is above \a maxColor \param minFailColor color to use for pixels that are below \a minColor for some settings of \a paletteMinFail \param maxFailColor color to use for pixels that are below \a maxColor for some settings of \a paletteMaxFail \param nanColor color to use for pixels that are not-a-number \param infColor color to use for pixels that are infinity \param logScale create a log-scaled image \param logBase base for the logarithm used when \c logScale==true \param lutUser user define LUT, used if \a palette \c ==JKQTPMathImageUSER_PALETTE \note There is a variant of this function that is called with a userLUT directly, instead of \a palette */ template static inline void array2image(const T* dbl_in, int width, int height, QImage &img, JKQTPMathImageColorPalette palette, double minColor, double maxColor, JKQTPMathImageColorRangeFailAction paletteMinFail=JKQTPMathImageLastPaletteColor, JKQTPMathImageColorRangeFailAction paletteMaxFail=JKQTPMathImageLastPaletteColor, QColor minFailColor=QColor("black"), QColor maxFailColor=QColor("black"), QColor nanColor=QColor("black"), QColor infColor=QColor("black"), bool logScale=false, double logBase=10.0, const LUTType& lutUser=LUTType()) { if (!dbl_in || width<=0 || height<=0) return; double min = *dbl_in; double max = *dbl_in; if (jkqtp_approximatelyEqual(minColor, maxColor, JKQTP_DOUBLE_EPSILON)) { bool first=true; for (int i=1; i(v)) || std::isinf(static_cast(v)))) { if (first) { min=max=v; first=false; } else { if (v < min) min = v; else if (v > max) max = v; } } } } else { min = minColor; max = maxColor; } const T* dbl=dbl_in; QVector dbl1; if (logScale) { double logB=log10(logBase); dbl1=QVector(width*height, 0); for (int i=0; i0) { // LUT found: collor the image accordingly for (int j=0; j(img.scanLine(height-1-j)); for (int i=0; i((val-min)/delta*static_cast(lutSize-1)); const int vv = (v < 0) ? 0 : ( (v >= lutSize) ? (lutSize-1) : v); line[i]=lut_used[vv]; if ((v<0)&&(paletteMinFail==JKQTPMathImageGivenColor)) { line[i]=minFailColor.rgba(); } else if ((v>lutSize)&&(paletteMaxFail==JKQTPMathImageGivenColor)) { line[i]=maxFailColor.rgba(); } else if ((v<0)&&(paletteMinFail==JKQTPMathImageTransparent)) { line[i]=QColor(Qt::transparent).rgba(); } else if ((v>lutSize)&&(paletteMaxFail==JKQTPMathImageTransparent)) { line[i]=QColor(Qt::transparent).rgba(); } } } } } else { // no LUT found: paint a black image! img.fill(0); } } } /*! \brief convert a 2D image (as 1D array) into a QImage with given palette (see JKQTFPColorPalette) \param dbl_in pointer to a 1D array of template type \c T representing the image to plot. This array has to be of size \a width * \a height \param width width of the array in \a dbl \param height height of the array in \a dbl \param[out] img the QImage object to draw to (should be initialized as \c QImage::Format_ARGB32 ) \param lutUser user-defined lookup-table \param minColor lower boundary of color range in \a dbl pixels, if \a minColor == \a maxColor then this function will extract the image min and image max. \param maxColor upper boundary of color range in \a dbl pixels, if \a minColor == \a maxColor then this function will extract the image min and image max. \param paletteMinFail specifies what shell happen, when a value in \a dbl is below \a minColor \param paletteMaxFail specifies what shell happen, when a value in \a dbl is above \a maxColor \param minFailColor color to use for pixels that are below \a minColor for some settings of \a paletteMinFail \param maxFailColor color to use for pixels that are below \a maxColor for some settings of \a paletteMaxFail \param nanColor color to use for pixels that are not-a-number \param infColor color to use for pixels that are infinity \param logScale create a log-scaled image \param logBase base for the logarithm used when \c logScale==true */ template static inline void array2image(const T* dbl_in, int width, int height, QImage &img, const LUTType& lutUser, double minColor, double maxColor, JKQTPMathImageColorRangeFailAction paletteMinFail=JKQTPMathImageLastPaletteColor, JKQTPMathImageColorRangeFailAction paletteMaxFail=JKQTPMathImageLastPaletteColor, QColor minFailColor=QColor("black"), QColor maxFailColor=QColor("black"), QColor nanColor=QColor("black"), QColor infColor=QColor("black"), bool logScale=false, double logBase=10.0) { array2image(dbl_in, width, height, img, JKQTPMathImageUSER_PALETTE, minColor, maxColor, paletteMinFail, paletteMaxFail, minFailColor, maxFailColor, nanColor, infColor, logScale, logBase, lutUser); } /** \brief return a list of all globally available LUTs, human-readable/localized form */ static QStringList JKQTCOMMON_LIB_EXPORT getPredefinedPalettes(); /** \brief return a list of all globally available LUTs, machine-readable form */ static QStringList JKQTCOMMON_LIB_EXPORT getPredefinedPalettesMachineReadable(); /*! \brief convert the palette \a p to a string \see JKQTPImageTools::String2JKQTPMathImageColorPalette() */ static JKQTCOMMON_LIB_EXPORT QString JKQTPMathImageColorPalette2String(JKQTPMathImageColorPalette p); /*! \brief convert the palette name \a p to JKQTPMathImageColorPalette (compatible with JKQTPImageTools::String2JKQTPMathImageColorPalette() ) \see JKQTPImageTools::JKQTPMathImageColorPalette2String() */ static JKQTCOMMON_LIB_EXPORT JKQTPMathImageColorPalette String2JKQTPMathImageColorPalette(const QString& p); /** \brief generates a QImage with width \a width and height 1 for the i-th color palette (\a i is based on the list returned by JKQTPImagePlot_getPredefinedPalettes() ) */ static QImage JKQTCOMMON_LIB_EXPORT GetPaletteImage(int i, int width); /** \brief generates a QImage with width \a width and height \a height for the i-th color palette (\a i is based on the list returned by JKQTPImagePlot_getPredefinedPalettes() ) */ static QImage JKQTCOMMON_LIB_EXPORT GetPaletteImage(int i, int width, int height); /** \brief generates a QImage with width \a width and height 1 for a specific JKQTPMathImageColorPalette */ static QImage JKQTCOMMON_LIB_EXPORT GetPaletteImage(JKQTPMathImageColorPalette palette, int width); /** \brief generates a QImage with width \a width and height \a height for a specific JKQTPMathImageColorPalette */ static QImage JKQTCOMMON_LIB_EXPORT GetPaletteImage(JKQTPMathImageColorPalette palette, int width, int height); /** \brief generates a QImage with width \a width and height 1 for a lookup-table \a lut */ static QImage JKQTCOMMON_LIB_EXPORT GetPaletteImage(const LUTType& lut, int width); /** \brief generates a QIcon for the i-th color palette (\a i is based on the list returned by JKQTPImagePlot_getPredefinedPalettes() ) */ static QIcon JKQTCOMMON_LIB_EXPORT GetPaletteIcon(int i) ; /** \brief generates a QIcon for a specific JKQTPMathImageColorPalette */ static QIcon JKQTCOMMON_LIB_EXPORT GetPaletteIcon(JKQTPMathImageColorPalette palette) ; private: /*! \brief internal datatype, representing a lookup-table and its metadata inside global_jkqtpimagetools_lutstore \internal */ struct LUTData { LUTData(); LUTData(const LUTType& _lut, const QString& _name, const QString& _nameT); LUTData(const QString& _name, const QString& _nameT); /** \brief the LUT itself */ LUTType lut; /** \brief name for the LUT (machine-readable) */ QString name; /** \brief name for the LUT (localized, human-readable) */ QString nameT; }; /*! \brief internal global storage object for lookup-tables \internal */ static JKQTCOMMON_LIB_EXPORT QMap global_jkqtpimagetools_lutstore; /*! \brief storage for the next ID to assign to a user-defined palette, registered with registerPalette() or registerPalettesFromFile() \internal \see registerPalette() registerPalettesFromFile() */ static JKQTCOMMON_LIB_EXPORT int global_next_userpalette; /** \brief Mutex to protect global_jkqtpimagetools_lutstore and global_next_userpalette */ static JKQTCOMMON_LIB_EXPORT std::mutex lutMutex; /*! \brief returns data of the default LUTs, used to initialize global_jkqtpimagetools_lutstore \internal */ static JKQTCOMMON_LIB_EXPORT QMap getDefaultLUTs(); /*! \brief create a LUT for a given JKQTPMathImageColorPalette, store it in \a lutstore and return it \internal */ static JKQTCOMMON_LIB_EXPORT const LUTType& getLUTforPalette(QMap &lutcache, JKQTPMathImageColorPalette palette); }; /*! \brief modes available for RGB images \ingroup jkqtplotter_imagelots_tools \see Examples: \ref JKQTPlotterRGBImagePlot */ enum JKQTPRGBMathImageRGBMode { JKQTPRGBMathImageModeRGBMode=0, /*!< image channels are mapped to the R, G and B channel (red-green-blue) */ JKQTPRGBMathImageModeHSVMode=1, /*!< image channels are mapped to the H, S and V channel (hue-saturation-value) */ JKQTPRGBMathImageModeHSLMode=2, /*!< image channels are mapped to the H, S and L channel (bue-saturation-luminance) */ JKQTPRGBMathImageModeCMYMode=3 /*!< image channels are mapped to the C, M and Y channel (subtractive color model!!!) */ }; /*! \brief returns a vector containing all elements of the given array as doubles \ingroup jkqtplotter_imagelots_tools */ template inline QVector JKQTPImagePlot_arrayToDVector(const T* input, int N) { if (!input || N<=0) return QVector(); QVector out(N, 0.0); for (int i=0; i JKQTPImagePlot_BarrayToDVector(const bool* input, int N) { if (!input || N<=0) return QVector(); QVector out(N, 0.0); for (int i=0; i inline double JKQTPImagePlot_getImageMin(const T* dbl, int width, int height) { if (!dbl || width<=0 || height<=0) return 0; double min = 0; double max = 0; bool first=true; for (int i=1; i(v)) || std::isinf(static_cast(v)))) { if (first) { min=max=v; first=false; } else { if (v < min) min = v; else if (v > max) max = v; } } } return min; }; /*! \brief fin the maximum pixel value in the given image \a dbl with width \a width and height \a height \ingroup jkqtplotter_imagelots_tools */ template inline double JKQTPImagePlot_getImageMax(const T* dbl, int width, int height) { if (!dbl || width<=0 || height<=0) return 0; double min = 0; double max = 0; bool first=true; for (int i=1; i(v)) || std::isinf(static_cast(v)))) { if (first) { min=max=v; first=false; } else { if (v < min) min = v; else if (v > max) max = v; } } } return max; }; /*! \brief convert a 2D image (as 1D array) into a QImage and puts the image values into one color channel (set by \a channel).The other color channels are not changed! So a repeated call to this function for the SAME QImage will result in a step-by-step buildup of an image. \ingroup jkqtplotter_imagelots_tools \note All calls (except channel>=3, i.e. alpha) set alpha to 255. Only the call with channel==3 (alpha) sets alpha to the desired value. Calls with channel==4 (saturation), channel==5 (value) leave alpha as it is. */ template inline void JKQTPImagePlot_array2RGBimage(const T* dbl_in, int width, int height, QImage &img, int channel, double minColor, double maxColor, JKQTPRGBMathImageRGBMode rgbMode=JKQTPRGBMathImageModeRGBMode, bool logScale=false, double logBase=10.0) { if (!dbl_in || width<=0 || height<=0) return; double min = *dbl_in; double max = *dbl_in; bool first=true; if (jkqtp_approximatelyEqual(minColor, maxColor, JKQTP_DOUBLE_EPSILON)) { for (int i=1; i(v))) { if (first) { min=max=v; first=false; } else { if (v < min) min = v; else if (v > max) max = v; } } } } else { min = minColor; max = maxColor; } const T* dbl=dbl_in; QVector dbllog; if (logScale) { double logB=log10(logBase); dbllog.resize(static_cast(width)*static_cast(height)); //memcpy(dbl, dbl_in, width*height*sizeof(T)); for (int i=0; i(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; //if (j==5) qDebug()<<"r: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; //if (j==5) qDebug()<<"g: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; //if (j==5) qDebug()<<"b: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; //if (j==5) qDebug()<<"b: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; //if (j==5) qDebug()<<"r: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; //if (j==5) qDebug()<<"g: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; //if (j==5) qDebug()<<"b: "<(img.scanLine(height-1-j)); for (int i=0; i 360) ? 360 : v); QColor l=QColor::fromRgb(line[i]); //if (i<10 && j==5) qDebug()<<"hi: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); QColor l=QColor::fromRgb(line[i]); //if (i<10 && j==5) qDebug()<<"si: "<(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); QColor l=QColor::fromRgb(line[i]); //if (i<10 && j==5) qDebug()<<"vi: "<(img.scanLine(height-1-j)); for (int i=0; i 360) ? 360 : v); QColor l=line[i]; l.setHsl(v, l.saturation(), l.lightness()); line[i]=l.rgb(); } } } else if (channel==1) { for (int j=0; j(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); QColor l=line[i]; l.setHsl(l.hue(), v, l.lightness()); line[i]=l.rgb(); } } } else if (channel==2) { for (int j=0; j(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); QColor l=line[i]; l.setHsl(l.hue(), l.saturation(), v); line[i]=l.rgb(); } } } } if (channel==3) { for (int j=0; j(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); const QRgb l=line[i]; line[i]=qRgba(qRed(l),qGreen(l),qBlue(l),v); } } } else if (channel==4) { for (int j=0; j(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); QColor c=QColor::fromRgba(line[i]); c.setHsv(c.hue(), v, c.value(), c.alpha()); line[i]=c.rgba(); } } } else if (channel==5) { for (int j=0; j(img.scanLine(height-1-j)); for (int i=0; i 255) ? 255 : v); QColor c=QColor::fromRgba(line[i]); c.setHsv(c.hue(), c.saturation(), v, c.alpha()); line[i]=c.rgba(); } } } } } /*! \brief for building palettes from linear segments of single colors using JKQTPBuildColorPaletteLUTLinSegmentsSorted() and JKQTPBuildColorPaletteLUTLinSegments() \ingroup jkqtplotter_imagelots_tools_LUTS \see JKQTPBuildColorPaletteLUTLinSegmentsSorted() and JKQTPBuildColorPaletteLUTLinSegments(), \ref JKQTPlotterImagePlotUserPalette */ struct JKQTPColorPaletteSingleColorLinSegment { JKQTPColorPaletteSingleColorLinSegment(double p, uint8_t y1, uint8_t y2); static JKQTPColorPaletteSingleColorLinSegment makeDbl_0_1(double p, double y1, double y2); JKQTPColorPaletteSingleColorLinSegment(); /** \brief scalar position of the element on the value axis */ double position; /** \brief color-channel-value that ends the prevoius segment (ignored for the first entry in a table) */ uint8_t colval_endprevious; /** \brief color-channel-value that starts the next segment (ignored for the last entry in a table) */ uint8_t colval_startnext; }; /*! \brief build an interpolated palette with \a lut_size entries from the linear segments defined for the color channels R, G and B in \a itemsR, \a itemG and \a itemB respectively \b NOTE: The entries in \a itemsR, \a itemsG, \a itemsB are assumed to be sorted by the position entry. \ingroup jkqtplotter_imagelots_tools_LUTS This is used to build a table of linear segments as a \c QList : \verbatim i position colval1 colval2 ~~~ ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ 0 0.0 0 (IGNORED) 0 1 0.5 100 100 2 0.8 255 255 3 1.0 255 255 (IGNORED) \endverbatim This will build a graph: \verbatim colval ^ | 250 - #**********# | *** 200 - *** | *** 150 - ** | ** 100 - *****# | **** 50 - ***** | ***** 0 - #***** | ---|----|----|----|----|----|----|----|----|----|----|--> position 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 \endverbatim You can also build graphs with a jump at a certain position \verbatim i position colval1 colval2 ~~~ ~~~~~~~~~~ ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ 0 0.0 0 (IGNORED) 0 1 0.5 100 0 2 1.0 255 255 (IGNORED) \endverbatim This results in: \verbatim colval ^ | 250 - **# | ** 200 - ** | ** 150 - *** | ** 100 - # ** | ****** ** 50 - ****** ** | ****** ** 0 - #****** #** | ---|----|----|----|----|----|----|----|----|----|----|--> position 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 \endverbatim \see JKQTPBuildColorPaletteLUTLinSegments(), \ref JKQTPlotterImagePlotUserPalette , https://matplotlib.org/api/_as_gen/matplotlib.colors.LinearSegmentedColormap.html#matplotlib.colors.LinearSegmentedColormap */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUTLinSegmentsSorted(const QList& itemsR, const QList& itemsG, const QList& itemsB, int lut_size=JKQTPImageTools::LUTSIZE); /*! \brief build a linearly interpolated palette (as a look-up table) with \a lut_size entries by linearly interpolating between the nodes in \a items . \b NOTE: \a items is assumed to be sorted by the first component of the \c QPair entries! \ingroup jkqtplotter_imagelots_tools_LUTS The LUT is built following these rules: - the final LUT has \a lut_size entries - the first color in the lut is given by \c items.first().second - the last color in the lut is given by \c items.last().second - in between the colors are interpolated between the nodes in \a items and the color-nodes are distributed according to the first component of the \c QPair entries:
\image html JKQTPBuildColorPaletteLUTLinInterpolateSorted.png . \see JKQTPBuildColorPaletteLUTLinInterpolate(), \ref JKQTPlotterImagePlotUserPalette */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUTLinInterpolateSorted(const QList >& items, int lut_size=JKQTPImageTools::LUTSIZE); /*! \brief build a palette (as a look-up table) with \a lut_size entries that step between the nodes provided in \a items. \b NOTE: \a items is assumed to be sorted by the first component of the \c QPair entries! \ingroup jkqtplotter_imagelots_tools_LUTS The LUT is built following these rules: - the final LUT has \a lut_size entries - the first color in the lut is given by \c items.first().second - the last color in the lut is given by \c items.last().second - in between the colors are stepped between the nodes in \a items and the color-nodes are distributed according to the first component of the \c QPair entries:
\image html JKQTPBuildColorPaletteLUTSorted.png . \see JKQTPBuildColorPaletteLUT(), \ref JKQTPlotterImagePlotUserPalette */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUTSorted(const QList >& items, int lut_size=JKQTPImageTools::LUTSIZE); /*! \brief like JKQTPBuildColorPaletteLUTLinInterpolateSorted(), but sorts \a items before processing it! \ingroup jkqtplotter_imagelots_tools_LUTS \copydetails JKQTPBuildColorPaletteLUTLinInterpolateSorted() */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUTLinInterpolate(QList > items, int lut_size=JKQTPImageTools::LUTSIZE); /*! \brief like JKQTPBuildColorPaletteLUTSorted(), but sorts \a items before processing it! \ingroup jkqtplotter_imagelots_tools_LUTS \copydetails JKQTPBuildColorPaletteLUTSorted() */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUT(QList > items, int lut_size=JKQTPImageTools::LUTSIZE); /*! \brief like JKQTPBuildColorPaletteLUTLinSegmentsSorted(), but sorts \a itemsR, \a itemB, \a itemsG before processing them! \ingroup jkqtplotter_imagelots_tools_LUTS \copydetails JKQTPBuildColorPaletteLUTLinSegmentsSorted() */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUTLinSegments(QList itemsR, QList itemsG, QList itemsB, int lut_size=JKQTPImageTools::LUTSIZE); /*! \brief like JKQTPBuildColorPaletteLUTLinInterpolateSorted(), but accepts a \c QMap as parameter instead of \c QList> \ingroup jkqtplotter_imagelots_tools_LUTS \copydetails JKQTPBuildColorPaletteLUTLinInterpolateSorted() */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUTLinInterpolate(const QMap& items, int lut_size=JKQTPImageTools::LUTSIZE); /*! \brief like JKQTPBuildColorPaletteLUTSorted(), but accepts a \c QMap as parameter instead of \c QList> \ingroup jkqtplotter_imagelots_tools_LUTS \copydetails JKQTPBuildColorPaletteLUTSorted() */ JKQTPImageTools::LUTType JKQTCOMMON_LIB_EXPORT JKQTPBuildColorPaletteLUT(const QMap& items, int lut_size=JKQTPImageTools::LUTSIZE); /** \brief describes how to modify a rendered image with a second data array \see ModifierModeToString(), StringToModifierMode(), JKQTPImageModifierModeComboBox \ingroup jkqtplotter_imagelots_tools*/ enum class JKQTPMathImageModifierMode { ModifyNone=0, /*!< no modification \image html JKQTPMathImageBaseModifyNone.png */ ModifyValue=1, /*!< modify the VALUE-channel from the HSV color space \image html JKQTPMathImageBaseModifyValue.png */ ModifySaturation=2,/*!< modify the SATURATION-channel from the HSV color space \image html JKQTPMathImageBaseModifySaturation.png */ ModifyAlpha=3,/*!< modify the ALPHA/TRANSPARENCY-channel from the RGBA color space \image html JKQTPMathImageBaseModifyAlpha.png */ ModifyTransparency=ModifyAlpha,/*!< \see ModifyAlpha */ ModifyLuminance=4,/*!< modify the LUMINANCE-channel from the HSL color space \image html JKQTPMathImageBaseModifyLuminance.png */ ModifyHue=5,/*!< modify the HUE-channel from the HSV color space \image html JKQTPMathImageBaseModifyHue.png */ }; /** \brief convert a ModifierMode to a string \ingroup jkqtplotter_imagelots_tools \see ModifierModeToString(), ModifierMode */ JKQTPMathImageModifierMode JKQTCOMMON_LIB_EXPORT StringToModifierMode(const QString& mode); /** \brief convert a string to a ModifierMode \ingroup jkqtplotter_imagelots_tools \see StringToModifierMode(), ModifierMode */ QString JKQTCOMMON_LIB_EXPORT ModifierModeToString(const JKQTPMathImageModifierMode& mode); /** \brief modify the given image \a img, using modifier image \a dataModifier (of type \a datatypeModifier and size \a Nx * \a Ny), using values in the range \a internalModifierMin ... \a internalModifierMax ) \ingroup jkqtplotter_imagelots_tools */ void JKQTCOMMON_LIB_EXPORT JKQTPModifyImage(QImage& img, JKQTPMathImageModifierMode modifierMode, const void* dataModifier, JKQTPMathImageDataType datatypeModifier, int Nx, int Ny, double internalModifierMin, double internalModifierMax); #endif // JKQTPBASICIMAGETOOLS_H