- RGB-image plots now work properly with inverted axes (image is inverted, befor image was not shown at all)

- added example for simple RGB math image plot with RGB/CMY-color mapping
- fixed some of the OpenCV examples (improved QMake-project files)
This commit is contained in:
jkriege2 2019-02-09 22:17:01 +01:00
parent 8ce6987315
commit 3050debd16
33 changed files with 435 additions and 45 deletions

1
.gitignore vendored
View File

@ -92,3 +92,4 @@ moc_predefs.h
/doc/*.tmp /doc/*.tmp
*.prl *.prl
Sicherungskopie_* Sicherungskopie_*
/examples/simpletest_rgbimageplot_opencv/opencv

View File

@ -64,6 +64,7 @@ addSimpleTest(symbols_and_errors)
addSimpleTest(symbols_and_styles) addSimpleTest(symbols_and_styles)
addSimpleTest(filledgraphs) addSimpleTest(filledgraphs)
addSimpleTest(speed) addSimpleTest(speed)
addSimpleTest(rgbimageplot)
addSimpleTest(rgbimageplot_qt) addSimpleTest(rgbimageplot_qt)
addSimpleTest(impulsesplot) addSimpleTest(impulsesplot)
addSimpleTest(paramscatterplot) addSimpleTest(paramscatterplot)

View File

@ -85,7 +85,7 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
<tr><th> Screenshot <th> Description <th> Notes <tr><th> Screenshot <th> Description <th> Notes
<tr><td> \image html jkqtplotter_simpletest_rgbimageplot_qt_small.png <tr><td> \image html jkqtplotter_simpletest_rgbimageplot_qt_small.png
<td> \subpage JKQTPlotterImagePlotQImageRGB <td> \subpage JKQTPlotterImagePlotQImageRGB
<td> `JKQTPImage` <br> `QImage` drawn onto a plot with arbitrary scaling) <td> `JKQTPImage` <br> `QImage` drawn onto a plot with arbitrary scaling <br> inverted coordinate axes
<tr><td> \image html jkqtplotter_simpletest_imageplot_small.png <tr><td> \image html jkqtplotter_simpletest_imageplot_small.png
<td> \subpage JKQTPlotterImagePlot <td> \subpage JKQTPlotterImagePlot
<td> `JKQTPColumnMathImage` <br> image data copied from C-style row-major array into a single column of the internal datastore <br> Describes several options of the image plotting classes (different ways of color coding, what to do with data above/below the limits etc.) <td> `JKQTPColumnMathImage` <br> image data copied from C-style row-major array into a single column of the internal datastore <br> Describes several options of the image plotting classes (different ways of color coding, what to do with data above/below the limits etc.)
@ -95,12 +95,15 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
<tr><td> \image html jkqtplotter_simpletest_imageplot_nodatastore_small.png <tr><td> \image html jkqtplotter_simpletest_imageplot_nodatastore_small.png
<td> \subpage JKQTPlotterImagePlotNoDatastore <td> \subpage JKQTPlotterImagePlotNoDatastore
<td> `JKQTPMathImage` <br> image data in a C-style row-major array, not using internal datastore <td> `JKQTPMathImage` <br> image data in a C-style row-major array, not using internal datastore
<tr><td> \image html jkqtplotter_simpletest_rgbimageplot_small.png
<td> \subpage JKQTPlotterRGBImagePlot
<td> `JKQTPColumnRGBMathImage` <br> image data in a C-style row-major array, not using internal datastore <br> RGB/CMY color compositing
<tr><td> \image html jkqtplotter_simpletest_imageplot_opencv_small.png <tr><td> \image html jkqtplotter_simpletest_imageplot_opencv_small.png
<td> \subpage JKQTPlotterImagePlotOpenCV <td> \subpage JKQTPlotterImagePlotOpenCV
<td> `JKQTPColumnMathImage` <br> image data copied from OpenCV cv::Mat-structure into a single column of the internal datastore <td> `JKQTPColumnMathImage` <br> image data copied from OpenCV cv::Mat-structure into a single column of the internal datastore
<tr><td> \image html jkqtplotter_simpletest_rgbimageplot_opencv_small.png <tr><td> \image html jkqtplotter_simpletest_rgbimageplot_opencv_small.png
<td> \subpage JKQTPlotterImagePlotRGBOpenCV <td> \subpage JKQTPlotterImagePlotRGBOpenCV
<td> `JKQTPColumnRGBMathImage` <br> image data copied from OpenCV cv::Mat-structure into three columns of the internal datastore <td> `JKQTPColumnRGBMathImage` <br> image data copied from OpenCV cv::Mat-structure into three columns of the internal datastore <br> inverted coordinate axes
</table> </table>

View File

@ -38,6 +38,7 @@ Changes, compared to \ref WHATSNEW_V2018_08 "v2018.08" include:
<li> new: frames (plot viewport, key/legend ...) may be rounded off at the corners</li> <li> new: frames (plot viewport, key/legend ...) may be rounded off at the corners</li>
<li> new: diverse new styling options (default font name/size ...)</li> <li> new: diverse new styling options (default font name/size ...)</li>
<li> new: additionl options for styling coordinate axes</li> <li> new: additionl options for styling coordinate axes</li>
<li> fixed: RGB-image plots now work properly with inverted axes (image is inverted, befor image was not shown at all) </li>
</ul></li> </ul></li>
<li> Updates to JKQTMathText: <li> Updates to JKQTMathText:
<ul> <ul>

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -37,12 +37,13 @@ All test-projects are Qt-projects that use qmake to build. You can load them int
| Screenshot | Description | Notes | | Screenshot | Description | Notes |
|:-------------:| ------------- | ------------- | |:-------------:| ------------- | ------------- |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_qt_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_qt) | [`QImage` as a Graph](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_qt) | `JKQTPImage` <br> `QImage` drawn onto a plot with arbitrary scaling) | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_qt_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_qt) | [`QImage` as a Graph](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_qt) | `JKQTPImage` <br> `QImage` drawn onto a plot with arbitrary scaling <br> inverted coordinate axes |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot) | [Basic 1-channel Raw C Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot) | `JKQTPColumnMathImage` <br> image data copied from C-style row-major array into a single column of the internal datastore <br> Describes several options of the image plotting classes (different ways of color coding, what to do with data above/below the limits etc.) | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot) | [Basic 1-channel Raw C Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot) | `JKQTPColumnMathImage` <br> image data copied from C-style row-major array into a single column of the internal datastore <br> Describes several options of the image plotting classes (different ways of color coding, what to do with data above/below the limits etc.) |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_modifier_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_modifier) | [Modifier-Feature of Image Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_modifier) | `JKQTPColumnMathImage` <br> image data copied from C-style row-major array into a single column of the internal datastore <br> Image is modified by a second image to display two data dimensions at the same time | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_modifier_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_modifier) | [Modifier-Feature of Image Plots](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_modifier) | `JKQTPColumnMathImage` <br> image data copied from C-style row-major array into a single column of the internal datastore <br> Image is modified by a second image to display two data dimensions at the same time |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_nodatastore_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_nodatastore) | [Basic 1-channel Raw C Image Plot <br> without the internal datastore](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_nodatastore) | `JKQTPMathImage` <br> image data in a C-style row-major array, not using internal datastore | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_nodatastore_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_nodatastore) | [Basic 1-channel Raw C Image Plot <br> without the internal datastore](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_nodatastore) | `JKQTPMathImage` <br> image data in a C-style row-major array, not using internal datastore |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/jkqtplotter_simpletest_rgbimageplot) | [Simple 3-channel Math RGB/CMY Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/jkqtplotter_simpletest_rgbimageplot) | `JKQTPColumnRGBMathImage` <br> image data in a C-style row-major array, not using internal datastore <br> RGB/CMY color compositing |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_opencv_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv) | [1-channel OpenCV cv::Mat Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv) | `JKQTPColumnMathImage` <br> image data copied from OpenCV cv::Mat-structure into a single column of the internal datastore | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_imageplot_opencv_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv) | [1-channel OpenCV cv::Mat Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv) | `JKQTPColumnMathImage` <br> image data copied from OpenCV cv::Mat-structure into a single column of the internal datastore |
| [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_opencv_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_opencv) | [RGB OpenCV cv::Mat Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_opencv) | `JKQTPColumnRGBMathImage` <br> image data copied from OpenCV cv::Mat-structure into three columns of the internal datastore | | [![](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_opencv_small.png)](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_opencv) | [RGB OpenCV cv::Mat Image Plot](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_rgbimageplot_opencv) | `JKQTPColumnRGBMathImage` <br> image data copied from OpenCV cv::Mat-structure into three columns <br> inverted coordinate axesof the internal datastore |

View File

@ -10,7 +10,7 @@
#include "jkqtplotter/jkqtpgraphs.h" #include "jkqtplotter/jkqtpgraphs.h"
#include "jkqtplotter/jkqtpgraphsimage.h" #include "jkqtplotter/jkqtpgraphsimage.h"
#include "jkqtplotter/jkqtpopencvinterface.h" #include "jkqtplotter/jkqtpopencvinterface.h"
#include <opencv/cv.h> #include <opencv2/opencv.hpp>
#ifndef M_PI #ifndef M_PI
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846

View File

@ -9,23 +9,26 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name # output executable name
TARGET = jkqtplotter_simpletest_imageplot_opencv TARGET = jkqtplotter_simpletest_imageplot_opencv
# add OpenCV-interface to JKQTPDatastore
DEFINES += JKQTPLOTTER_OPENCV_INTERFACE
# link agains OpenCV-3.4.1 # link agains OpenCV-3.4.1
INCLUDEPATH += $$PWD/OpenCV-3.4.1/include/ #INCLUDEPATH += $$PWD/../../../OpenCV-3.4.1/include/
LIBS += -L$$PWD/OpenCV-3.4.1/bin/ -llibopencv_core341 #LIBS += -L$$PWD/../../../OpenCV-3.4.1/bin/ -llibopencv_core341 -llibopencv_imgcodecs341
#opencvdlls.files=$$PWD/../../../OpenCV-3.4.1/bin/*.dll
#opencvdlls.path=$$OUT_PWD
# link agains OpenCV-4
INCLUDEPATH += $$PWD/../../../OpenCV-4.0.1/include/
LIBS += -L$$PWD/../../../OpenCV-4.0.1/x64/mingw/bin -L$$PWD/../../../OpenCV-4.0.1/x64/mingw/lib -llibopencv_core400.dll -llibopencv_imgcodecs400.dll
opencvdlls.files=$$PWD/../../../OpenCV-4.0.1/x64/mingw/bin/*.dll
opencvdlls.path=$$OUT_PWD
INCLUDEPATH+=../../lib
CONFIG (debug, debug|release) { CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else { } else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
} }
message("LIBS = $$LIBS") message("LIBS = $$LIBS")
INSTALLS += opencvdlls

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_imageplot_opencv
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_imageplot_opencv.file=$$PWD/jkqtplotter_simpletest_imageplot_opencv.pro
jkqtplotter_simpletest_imageplot_opencv.depends = jkqtplotterlib

View File

@ -0,0 +1,129 @@
# Example (JKQTPlotter): Simple Math RGB/CMY Image Plot {#JKQTPlotterRGBImagePlot}
This project (see `./examples/simpletest_imageplot/`) simply creates a JKQTPlotter widget (as a new window) and adds an image plot of a mathematical function (here the Airy disk). The function is calculated with different parameters and then the result for each parameter is mapped onto a separate color channel in the output. The image is stored as a simple C-array in row-major ordering and then copied into a single column of the internal datasdtore (JKQTPMathImage could be directly used without the internal datastore). This very simple interface can also be used to interface with many common image processing libraries, like CImg or OpenCV.
The source code of the main application is (see [`jkqtplotter_simpletest_rgbimageplot.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot/jkqtplotter_simpletest_rgbimageplot.cpp):
```.cpp
#include <QApplication>
#include <cmath>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsimagergb.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
JKQTPlotter plot;
// 1. create a plotter window and get a pointer to the internal datastore (for convenience)
plot.getPlotter()->setUseAntiAliasingForGraphs(true); // nicer (but slower) plotting
plot.getPlotter()->setUseAntiAliasingForSystem(true); // nicer (but slower) plotting
plot.getPlotter()->setUseAntiAliasingForText(true); // nicer (but slower) text rendering
JKQTPDatastore* ds=plot.getDatastore();
// 2. now we create data for the charts (taken from https://commons.wikimedia.org/wiki/File:Energiemix_Deutschland.svg)
const int NX=100; // image dimension in x-direction [pixels]
const int NY=100; // image dimension in x-direction [pixels]
const double dx=1e-2; // size of a pixel in x-direction [micrometers]
const double dy=1e-2; // size of a pixel in x-direction [micrometers]
const double w=static_cast<double>(NX)*dx;
const double h=static_cast<double>(NY)*dy;
double airydisk1[NX*NY]; // row-major image
double airydisk2[NX*NY]; // row-major image
// 2.1 Parameters for airy disk plot (see https://en.wikipedia.org/wiki/Airy_disk)
double NA=1.1; // numerical aperture of lens
double wavelength1=540e-3; // wavelength of the light [micrometers]
double wavelength2=450e-3; // wavelength of the light [micrometers]
// 2.2 calculate image of airy disk in a row-major array
double x, y=-h/2.0;
for (int iy=0; iy<NY; iy++ ) {
x=-w/2.0;
for (int ix=0; ix<NX; ix++ ) {
const double r=sqrt(x*x+y*y);
const double v1=2.0*M_PI*NA*r/wavelength1;
airydisk1[iy*NX+ix] = sqrt(pow(2.0*j1(v1)/v1, 2));
const double v2=2.0*M_PI*NA*r/wavelength2;
airydisk2[iy*NX+ix] = sqrt(pow(2.0*j1(v2)/v2, 2));
x+=dx;
}
y+=dy;
}
// 3. make data available to JKQTPlotter by adding it to the internal datastore.
// In this step the contents of C-array airydisk is copied into a column
// of the datastore in row-major order
size_t cAiryDisk1=ds->addCopiedImageAsColumn(airydisk1, NX, NY, "imagedata1");
size_t cAiryDisk2=ds->addCopiedImageAsColumn(airydisk2, NX, NY, "imagedata2");
// 4. create a graph (JKQTPColumnMathImage) with the column created above as data
// The data is color-coded with the color-palette JKQTPMathImageMATLAB
// the converted range of data is determined automatically because s etAutoImageRange(true)
JKQTPColumnRGBMathImage* graph=new JKQTPColumnRGBMathImage(&plot);
graph->setTitle("");
// image column with the data (R/G/B or C/M/Y ...)
graph->setImageGColumn(cAiryDisk1); // G/M channel
graph->setImageBColumn(cAiryDisk2); // B/Y channel
// set size of the data (the datastore does not contain this info, as it only manages 1D columns of data and this is used to assume a row-major ordering
graph->setNx(NX);
graph->setNy(NY);
// where does the image start in the plot, given in plot-axis-coordinates (bottom-left corner)
graph->setX(-w/2.0);
graph->setY(-h/2.0);
// width and height of the image in plot-axis-coordinates
graph->setWidth(w);
graph->setHeight(h);
// get coordinate axis of color-bar and set its label
graph->getColorBarRightAxisB()->setAxisLabel("blue light field strength [AU]");
graph->getColorBarRightAxisG()->setAxisLabel("green light field strength [AU]");
// determine min/max of data automatically and use it to set the range of the color-scale
graph->setAutoImageRange(true);
// 5. add the graphs to the plot, so it is actually displayed
plot.addGraph(graph);
// 6. set axis labels
plot.getXAxis()->setAxisLabel("x [{\\mu}m]");
plot.getYAxis()->setAxisLabel("y [{\\mu}m]");
// 7. fix axis and plot aspect ratio to 1
plot.getPlotter()->setMaintainAspectRatio(true);
plot.getPlotter()->setMaintainAxisAspectRatio(true);
// 8 autoscale the plot so the graph is contained
plot.zoomToFit();
// show plotter and make it a decent size
plot.show();
plot.resize(600,600);
plot.setWindowTitle("JKQTPColumnRGBMathImage");
return app.exec();
}
```
The result looks like this:
![jkqtplotter_simpletest_rgbimageplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot.png)
In the example above, we calculated two airy disks for two wavelengths and assigned them to the R and G color channel of the output image. Alternatively you can also assign them to the CMY-channels of the output image:
```.cpp
// use (subtractive) CMY color model, not RGB
graph->setRgbMode(JKQTPRGBMathImageModeCMYMode);
```
The result will then look like this:
![jkqtplotter_simpletest_rgbimageplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_cmy.png)
Note that the CMY-color model is a subtractive color model, whereas RGB is an additive model. Therefore CMY-color-scales range from CMY to white, whereas the RGB-scales range from RGB to black!

View File

@ -0,0 +1,116 @@
/** \example jkqtplotter_simpletest_rgbimageplot.cpp
* Shows how to plot colored math images with JKQTPlotter, where different images/matrices are assigned to different color channels
*
* \ref JKQTPlotterRGBImagePlot
*/
#include <QApplication>
#include <cmath>
#include "jkqtplotter/jkqtplotter.h"
#include "jkqtplotter/jkqtpgraphsimagergb.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
JKQTPlotter plot;
// 1. create a plotter window and get a pointer to the internal datastore (for convenience)
plot.getPlotter()->setUseAntiAliasingForGraphs(true); // nicer (but slower) plotting
plot.getPlotter()->setUseAntiAliasingForSystem(true); // nicer (but slower) plotting
plot.getPlotter()->setUseAntiAliasingForText(true); // nicer (but slower) text rendering
JKQTPDatastore* ds=plot.getDatastore();
// 2. now we create data for the charts (taken from https://commons.wikimedia.org/wiki/File:Energiemix_Deutschland.svg)
const int NX=100; // image dimension in x-direction [pixels]
const int NY=100; // image dimension in x-direction [pixels]
const double dx=1e-2; // size of a pixel in x-direction [micrometers]
const double dy=1e-2; // size of a pixel in x-direction [micrometers]
const double w=static_cast<double>(NX)*dx;
const double h=static_cast<double>(NY)*dy;
double airydisk1[NX*NY]; // row-major image
double airydisk2[NX*NY]; // row-major image
// 2.1 Parameters for airy disk plot (see https://en.wikipedia.org/wiki/Airy_disk)
double NA=1.1; // numerical aperture of lens
double wavelength1=540e-3; // wavelength of the light [micrometers]
double wavelength2=450e-3; // wavelength of the light [micrometers]
// 2.2 calculate image of airy disk in a row-major array
double x, y=-h/2.0;
for (int iy=0; iy<NY; iy++ ) {
x=-w/2.0;
for (int ix=0; ix<NX; ix++ ) {
const double r=sqrt(x*x+y*y);
const double v1=2.0*M_PI*NA*r/wavelength1;
airydisk1[iy*NX+ix] = sqrt(pow(2.0*j1(v1)/v1, 2));
const double v2=2.0*M_PI*NA*r/wavelength2;
airydisk2[iy*NX+ix] = sqrt(pow(2.0*j1(v2)/v2, 2));
x+=dx;
}
y+=dy;
}
// 3. make data available to JKQTPlotter by adding it to the internal datastore.
// In this step the contents of C-array airydisk is copied into a column
// of the datastore in row-major order
size_t cAiryDisk1=ds->addCopiedImageAsColumn(airydisk1, NX, NY, "imagedata1");
size_t cAiryDisk2=ds->addCopiedImageAsColumn(airydisk2, NX, NY, "imagedata2");
// 4. create a graph (JKQTPColumnMathImage) with the column created above as data
// The data is color-coded with the color-palette JKQTPMathImageMATLAB
// the converted range of data is determined automatically because s etAutoImageRange(true)
JKQTPColumnRGBMathImage* graph=new JKQTPColumnRGBMathImage(&plot);
graph->setTitle("");
// image column with the data (R/G/B or C/M/Y ...)
graph->setImageGColumn(cAiryDisk1); // G/M channel
graph->setImageBColumn(cAiryDisk2); // B/Y channel
// set size of the data (the datastore does not contain this info, as it only manages 1D columns of data and this is used to assume a row-major ordering
graph->setNx(NX);
graph->setNy(NY);
// where does the image start in the plot, given in plot-axis-coordinates (bottom-left corner)
graph->setX(-w/2.0);
graph->setY(-h/2.0);
// width and height of the image in plot-axis-coordinates
graph->setWidth(w);
graph->setHeight(h);
// get coordinate axis of color-bar and set its label
graph->getColorBarRightAxisB()->setAxisLabel("blue light field strength [AU]");
graph->getColorBarRightAxisG()->setAxisLabel("green light field strength [AU]");
// determine min/max of data automatically and use it to set the range of the color-scale
graph->setAutoImageRange(true);
// use (subtractive) CMY color model, not RGB
//graph->setRgbMode(JKQTPRGBMathImageModeCMYMode);
// 5. add the graphs to the plot, so it is actually displayed
plot.addGraph(graph);
// 6. set axis labels
plot.getXAxis()->setAxisLabel("x [{\\mu}m]");
plot.getYAxis()->setAxisLabel("y [{\\mu}m]");
// 7. fix axis and plot aspect ratio to 1
plot.getPlotter()->setMaintainAspectRatio(true);
plot.getPlotter()->setMaintainAxisAspectRatio(true);
// 8 autoscale the plot so the graph is contained
plot.zoomToFit();
// show plotter and make it a decent size
plot.show();
plot.resize(600,600);
plot.setWindowTitle("JKQTPColumnRGBMathImage");
return app.exec();
}

View File

@ -0,0 +1,25 @@
# source code for this simple demo
SOURCES = jkqtplotter_simpletest_rgbimageplot.cpp
# configure Qt
CONFIG += link_prl qt
QT += core gui xml svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name
TARGET = jkqtplotter_simpletest_rgbimageplot
# include JKQTPlotter source code
DEPENDPATH += ../../lib ../../staticlib/jkqtplotterlib
INCLUDEPATH += ../../lib
CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
}
message("LIBS = $$LIBS")

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_rgbimageplot
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_rgbimageplot.file=$$PWD/jkqtplotter_simpletest_rgbimageplot.pro
jkqtplotter_simpletest_rgbimageplot.depends = jkqtplotterlib

View File

@ -1,8 +1,8 @@
# Example (JKQTPlotter): Simple RGB image plot, showing a 3-channel OpenCV cv::Mat {#JKQTPlotterImagePlotRGBOpenCV} # Example (JKQTPlotter): Simple RGB image plot, showing a 3-channel OpenCV cv::Mat {#JKQTPlotterImagePlotRGBOpenCV}
This project (see `./examples/simpletest_imageplot_opencv/`) simply creates a JKQTPlotter widget (as a new window) and adds a color-coded image plot of a mathematical function (here the Airy disk). The image is generated as an OpenCV cv::Mat image and then copied into a single column of the internal datasdtore (JKQTPMathImage could be directly used without the internal datastore). This project (see `./examples/simpletest_imageplot_opencv/`) simply creates a JKQTPlotter widget (as a new window) and shows an RGB image read from a BMP-file. The image is generated as an [OpenCV](https://opencv.org/) [`cv::Mat`](https://docs.opencv.org/4.0.0/d3/d63/classcv_1_1Mat.html) image and then copied into a single column of the internal datasdtore (JKQTPMathImage could be directly used without the internal datastore).
To copy the data a special OpenCV Interface function `JKQTPCopyCvMatToColumn()` is used, that copies the data from a cv::Mat directly into a column. To copy the data a special OpenCV Interface function `JKQTPCopyCvMatToColumn()` is used, that copies the data from a (https://opencv.org/) [`cv::Mat`](https://docs.opencv.org/4.0.0/d3/d63/classcv_1_1Mat.html) directly into a column.
The function `JKQTPCopyCvMatToColumn()` is available from the (non-default) header-only extension from `jkqtplotter/jkqtpopencvinterface.h`. This header provides facilities to interface JKQTPlotter with OPenCV. The function `JKQTPCopyCvMatToColumn()` is available from the (non-default) header-only extension from `jkqtplotter/jkqtpopencvinterface.h`. This header provides facilities to interface JKQTPlotter with OpenCV.
The source code of the main application is (see [`jkqtplotter_simpletest_imageplot_opencv.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv/jkqtplotter_simpletest_imageplot_opencv.cpp): The source code of the main application is (see [`jkqtplotter_simpletest_imageplot_opencv.cpp`](https://github.com/jkriege2/JKQtPlotter/tree/master/examples/simpletest_imageplot_opencv/jkqtplotter_simpletest_imageplot_opencv.cpp):
```.cpp ```.cpp
@ -99,4 +99,15 @@ The result looks like this:
The image is upside-down, because computer images use a coordinate system with 0 at the top-left (left-handed coordinate system) and the JKQTPlotter has its 0 at the bottom-left (right-handed coordinate system). The image is upside-down, because computer images use a coordinate system with 0 at the top-left (left-handed coordinate system) and the JKQTPlotter has its 0 at the bottom-left (right-handed coordinate system).
You can modify the program above to display the image in the correct orientation, by adding the line
```.cpp
// 5.1 invert y-axis, so image is oriented correctly
plot.getYAxis()->setInverted(true);
```
This will reorient the y-axis to point from top to bottom (for increasing positive coordinates):
![jkqtplotter_simpletest_imageplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_opencv_updisdedown.png)

View File

@ -10,6 +10,7 @@
#include "jkqtplotter/jkqtpgraphs.h" #include "jkqtplotter/jkqtpgraphs.h"
#include "jkqtplotter/jkqtpgraphsimagergb.h" #include "jkqtplotter/jkqtpgraphsimagergb.h"
#include "jkqtplotter/jkqtpopencvinterface.h" #include "jkqtplotter/jkqtpopencvinterface.h"
#include <opencv2/opencv.hpp>
#include <opencv2/imgcodecs.hpp> #include <opencv2/imgcodecs.hpp>
@ -30,6 +31,7 @@ int main(int argc, char* argv[])
// 2. now we open a BMP-file and load it into an OpenCV cv::Mat // 2. now we open a BMP-file and load it into an OpenCV cv::Mat
cv::Mat picture = cv::imread("example.bmp"); cv::Mat picture = cv::imread("example.bmp");
qDebug()<<picture.rows<<"x"<<picture.cols<<"x"<<picture.channels();
@ -73,6 +75,8 @@ int main(int argc, char* argv[])
// 6. set axis labels // 6. set axis labels
plot.getXAxis()->setAxisLabel("x [pixels]"); plot.getXAxis()->setAxisLabel("x [pixels]");
plot.getYAxis()->setAxisLabel("y [pixels]"); plot.getYAxis()->setAxisLabel("y [pixels]");
// 6.1 invert y-axis, so image is oriented correctly
plot.getYAxis()->setInverted(true);
// 7. fix axis aspect ratio to width/height, so pixels are square // 7. fix axis aspect ratio to width/height, so pixels are square
plot.getPlotter()->setMaintainAspectRatio(true); plot.getPlotter()->setMaintainAspectRatio(true);

View File

@ -9,22 +9,29 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
# output executable name # output executable name
TARGET = jkqtplotter_simpletest_rgbimageplot_opencv TARGET = jkqtplotter_simpletest_rgbimageplot_opencv
# add OpenCV-interface to JKQTPDatastore exampleimg.files=$$PWD/example.bmp
DEFINES += JKQTPLOTTER_OPENCV_INTERFACE exampleimg.path=$$OUT_PWD
# link agains OpenCV-3.4.1 # link agains OpenCV-3.4.1
INCLUDEPATH += $$PWD/OpenCV-3.4.1/include/ #INCLUDEPATH += $$PWD/../../../OpenCV-3.4.1/include/
LIBS += -L$$PWD/OpenCV-3.4.1/bin/ -llibopencv_core341 -llibopencv_imgcodecs341 #LIBS += -L$$PWD/../../../OpenCV-3.4.1/bin/ -llibopencv_core341 -llibopencv_imgcodecs341
#opencvdlls.files=$$PWD/../../../OpenCV-3.4.1/bin/*.dll
#opencvdlls.path=$$OUT_PWD
# link agains OpenCV-4
INCLUDEPATH += $$PWD/../../../OpenCV-4.0.1/include/
LIBS += -L$$PWD/../../../OpenCV-4.0.1/x64/mingw/bin -L$$PWD/../../../OpenCV-4.0.1/x64/mingw/lib -llibopencv_core400.dll -llibopencv_imgcodecs400.dll
opencvdlls.files=$$PWD/../../../OpenCV-4.0.1/x64/mingw/bin/*.dll
opencvdlls.path=$$OUT_PWD
INCLUDEPATH+=../../lib
CONFIG (debug, debug|release) { CONFIG (debug, debug|release) {
LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug LIBS += -L../../staticlib/jkqtplotterlib/debug -ljkqtplotterlib_debug
} else { } else {
LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib LIBS += -L../../staticlib/jkqtplotterlib/release -ljkqtplotterlib
} }
message("LIBS = $$LIBS") message("LIBS = $$LIBS")
INSTALLS += opencvdlls exampleimg

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS += jkqtplotterlib jkqtplotter_simpletest_rgbimageplot_opencv
jkqtplotterlib.file = ../../staticlib/jkqtplotterlib/jkqtplotterlib.pro
jkqtplotter_simpletest_rgbimageplot_opencv.file=$$PWD/jkqtplotter_simpletest_rgbimageplot_opencv.pro
jkqtplotter_simpletest_rgbimageplot_opencv.depends = jkqtplotterlib

View File

@ -28,4 +28,16 @@ The result looks like this:
![jkqtplotter_simpletest_imageplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_qt.png) ![jkqtplotter_simpletest_imageplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_qt.png)
The image is upside-down, because computer images use a coordinate system with 0 at the top-left (left-handed coordinate system) and the JKQTPlotter has its 0 at the bottom-left (right-handed coordinate system).
You can modify the program above to display the image in the correct orientation, by adding the line
```.cpp
// 6.1 invert y-axis, so image is oriented correctly
plot.getYAxis()->setInverted(true);
```
This will reorient the y-axis to point from top to bottom (for increasing positive coordinates):
![jkqtplotter_simpletest_imageplot](https://raw.githubusercontent.com/jkriege2/JKQtPlotter/master/screenshots/jkqtplotter_simpletest_rgbimageplot_qt_updisdedown.png)

View File

@ -51,6 +51,8 @@ int main(int argc, char* argv[])
// 5. set axis labels // 5. set axis labels
plot.getXAxis()->setAxisLabel("x [pixels]"); plot.getXAxis()->setAxisLabel("x [pixels]");
plot.getYAxis()->setAxisLabel("y [pixels]"); plot.getYAxis()->setAxisLabel("y [pixels]");
// 5.1 invert y-axis, so image is oriented correctly
plot.getYAxis()->setInverted(true);
// 6. fix axis aspect ratio to width/height, so pixels are square // 6. fix axis aspect ratio to width/height, so pixels are square
plot.getPlotter()->setMaintainAspectRatio(true); plot.getPlotter()->setMaintainAspectRatio(true);

View File

@ -105,6 +105,16 @@ class JKQTBasePlotter;
are implemented in p2x(). They can be used to show the system coordinates of the current mouse position. are implemented in p2x(). They can be used to show the system coordinates of the current mouse position.
\section jkqtplotter_coordinateaxes_inverted Inverted Coordinate Axes
In some cases it may be necessary to invert the direction of increasing coordinates on an axis. One such case is image analysis, as computer images usually
have coordinates starting with (0,0) at the top left and increasing to the right (x) and down (y). You can invert any axis by setting \c setInverted(true).
\image html jkqtplotter_inverted_yaxis.png
\see You can find example here: \ref JKQTPlotterImagePlotQImageRGB and \ref JKQTPlotterImagePlotRGBOpenCV
\section jkqtplotter_base_grids_baseelemenets Axis JKQTPCoordinateAxisStyle::Ticks and Grids \section jkqtplotter_base_grids_baseelemenets Axis JKQTPCoordinateAxisStyle::Ticks and Grids
This section explains how this component draws the JKQTPCoordinateAxisStyle::ticks on coordinate axes and the grids that may be drawn below This section explains how this component draws the JKQTPCoordinateAxisStyle::ticks on coordinate axes and the grids that may be drawn below
@ -708,7 +718,10 @@ class JKQTP_LIB_EXPORT JKQTPCoordinateAxis: public QObject {
* \see calcPlotScaling(), calcTickSpacing() * \see calcPlotScaling(), calcTickSpacing()
*/ */
double offset; double offset;
/** \brief indicates whether the axis is to be inverted or not */ /** \brief indicates whether the axis is to be inverted or not
*
* \image html jkqtplotter_inverted_yaxis.png
*/
bool inverted; bool inverted;
/** \brief <b>calculated property:</b> x position of the first tick (calculated by calcPlotScaling() ). Given in system coordinates, not pixel coordinates. /** \brief <b>calculated property:</b> x position of the first tick (calculated by calcPlotScaling() ). Given in system coordinates, not pixel coordinates.

View File

@ -34,9 +34,6 @@
#include <QStringList> #include <QStringList>
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QObject> #include <QObject>
#ifdef JKQTPLOTTER_OPENCV_INTERFACE
# include <opencv/cv.h>
#endif
#ifndef JKQTPDATASTORAGE_H #ifndef JKQTPDATASTORAGE_H
#define JKQTPDATASTORAGE_H #define JKQTPDATASTORAGE_H

View File

@ -97,7 +97,7 @@ QColor JKQTPImageBase::getKeyLabelColor() {
} }
void JKQTPImageBase::plotImage(JKQTPEnhancedPainter& painter, QImage& image, double x, double y, double width, double height) { void JKQTPImageBase::plotImage(JKQTPEnhancedPainter& painter, QImage& image, double x, double y, double width, double height) {
if ((!JKQTPIsOKFloat(x))||(!JKQTPIsOKFloat(y))||(!JKQTPIsOKFloat(width))||(!JKQTPIsOKFloat(height))||(width<=0) || (height<=0) || image.isNull() || (image.width()<=0) || (image.height()<=0)) { if ((!JKQTPIsOKFloat(x))||(!JKQTPIsOKFloat(y))||(!JKQTPIsOKFloat(width))||(!JKQTPIsOKFloat(height))||(width==0) || (height==0) || image.isNull() || (image.width()<=0) || (image.height()<=0)) {
return; return;
} }
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
@ -110,13 +110,37 @@ void JKQTPImageBase::plotImage(JKQTPEnhancedPainter& painter, QImage& image, dou
QPointF pp2=transform(xmax,ymin); QPointF pp2=transform(xmax,ymin);
QRectF pr(pp1, pp2); QRectF pr(pp1, pp2);
bool mirrx=false;
bool mirry=false;
QPointF p1=transform(x,y+height); QPointF p1=transform(x,y+height);
QPointF p2=transform(x+width,y); QPointF p2=transform(x+width,y);
if (p1.x()>p2.x()) {
double tmp=p1.x();
p1.setX(p2.x());
p2.setX(tmp);
tmp=pp1.x();
pp1.setX(pp2.x());
pp2.setX(tmp);
mirrx=true;
}
if (p1.y()>p2.y()) {
double tmp=p1.y();
p1.setY(p2.y());
p2.setY(tmp);
tmp=pp1.y();
pp1.setY(pp2.y());
pp2.setY(tmp);
mirry=true;
}
QRectF r(p1, p2); QRectF r(p1, p2);
if (image.width()>0 && image.height()>0 && !image.isNull()) { if (image.width()>0 && image.height()>0 && !image.isNull()) {
if (r.width()<2*pr.width() && r.height()<2*pr.height()) { if (r.width()<2*pr.width() && r.height()<2*pr.height()) {
//painter.drawImage(QRectF(p1.x(), p2.y(), fabs(p2.x()-p1.x()), fabs(p2.y()-p1.y())), image); //painter.drawImage(QRectF(p1.x(), p2.y(), fabs(p2.x()-p1.x()), fabs(p2.y()-p1.y())), image);
painter.drawImage(QPoint(p1.x(), p1.y()), image.scaled(QSize(fabs(p2.x()-p1.x()), fabs(p2.y()-p1.y())), Qt::IgnoreAspectRatio, Qt::FastTransformation)); painter.drawImage(QPoint(p1.x(), p1.y()), image.mirrored(mirrx, mirry).scaled(QSize(fabs(p2.x()-p1.x()), fabs(p2.y()-p1.y())), Qt::IgnoreAspectRatio, Qt::FastTransformation));
//qDebug()<<"\nimage.size = "<<image.size() <<" SIMPLE!"; //qDebug()<<"\nimage.size = "<<image.size() <<" SIMPLE!";
} else { } else {
double pixwidth=fabs(p2.x()-p1.x())/static_cast<double>(image.width()); double pixwidth=fabs(p2.x()-p1.x())/static_cast<double>(image.width());
@ -136,7 +160,7 @@ void JKQTPImageBase::plotImage(JKQTPEnhancedPainter& painter, QImage& image, dou
QRectF target(p1.x()+ps1.x()*pixwidth, p1.y()+ps1.y()*pixheight, source.width()*pixwidth, source.height()*pixheight); QRectF target(p1.x()+ps1.x()*pixwidth, p1.y()+ps1.y()*pixheight, source.width()*pixwidth, source.height()*pixheight);
//qDebug()<<"source = "<<source; //qDebug()<<"source = "<<source;
//qDebug()<<"target = "<<target; //qDebug()<<"target = "<<target;
painter.drawImage(target, image, source); painter.drawImage(target, image.mirrored(mirrx, mirry), source);
} }
} }
@ -212,7 +236,7 @@ void JKQTPImage::drawKeyMarker(JKQTPEnhancedPainter &painter, QRectF &rect)
void JKQTPImage::setImage(const QImage &image) void JKQTPImage::setImage(const QImage &image)
{ {
clear_image(); clear_image();
this->image=new QImage(image); this->image=new QImage(image.mirrored(false, true));
image_owned=true; image_owned=true;
createImageActions(); createImageActions();
} }

View File

@ -911,7 +911,14 @@ void JKQTPColumnRGBMathImage::ensureImageData()
this->data=parent->getDatastore()->getColumn(imageRColumn).getPointer(0); this->data=parent->getDatastore()->getColumn(imageRColumn).getPointer(0);
this->dataG=parent->getDatastore()->getColumn(imageGColumn).getPointer(0); this->dataG=parent->getDatastore()->getColumn(imageGColumn).getPointer(0);
this->dataB=parent->getDatastore()->getColumn(imageBColumn).getPointer(0); this->dataB=parent->getDatastore()->getColumn(imageBColumn).getPointer(0);
this->Ny=parent->getDatastore()->getColumn(imageRColumn).getRows()/this->Nx; /*if (Nx*Ny==0 || Nx*Ny>parent->getDatastore()->getColumn(imageRColumn).getRows()) {
if (Nx>0) {
Ny=parent->getDatastore()->getColumn(imageRColumn).getRows()/this->Nx;
} else {
Nx=parent->getDatastore()->getColumn(imageRColumn).getRows();
Ny=1;
}
}*/
this->datatypeModifier=JKQTPMathImageBase::DoubleArray; this->datatypeModifier=JKQTPMathImageBase::DoubleArray;
this->dataModifier=parent->getDatastore()->getColumn(modifierColumn).getPointer(0); this->dataModifier=parent->getDatastore()->getColumn(modifierColumn).getPointer(0);
} }

View File

@ -518,7 +518,12 @@ class JKQTP_LIB_EXPORT JKQTPRGBMathImage: public JKQTPMathImageBase {
\image html rgbimageplots.png \image html rgbimageplots.png
\image html jkqtplotter_simpletest_rgbimageplot_opencv.png \image html jkqtplotter_simpletest_rgbimageplot_opencv.png
\image html jkqtplotter_simpletest_rgbimageplot.png
\see Examples: \ref JKQTPlotterRGBImagePlot and \ref JKQTPlotterImagePlotRGBOpenCV
*/ */
class JKQTP_LIB_EXPORT JKQTPColumnRGBMathImage: public JKQTPRGBMathImage { class JKQTP_LIB_EXPORT JKQTPColumnRGBMathImage: public JKQTPRGBMathImage {
Q_OBJECT Q_OBJECT

View File

@ -20,8 +20,8 @@
#include "jkqtplottertools/jkqtp_imexport.h" #include "jkqtplottertools/jkqtp_imexport.h"
#include "jkqtplotter/jkqtpdatastore.h" #include "jkqtplotter/jkqtpdatastorage.h"
#include <opencv/cv.h> #include <opencv2/core.hpp>
#ifndef JKQTPOPENCVINTERFACE_H #ifndef JKQTPOPENCVINTERFACE_H
#define JKQTPOPENCVINTERFACE_H #define JKQTPOPENCVINTERFACE_H

View File

@ -164,12 +164,16 @@ enum JKQTPMathImageColorRangeFailAction {
/*! \brief modes available for RGB images /*! \brief modes available for RGB images
\ingroup jkqtplotter_imagelots_tools \ingroup jkqtplotter_imagelots_tools
\see Examples: \ref JKQTPlotterRGBImagePlot
*/ */
enum JKQTPRGBMathImageRGBMode { enum JKQTPRGBMathImageRGBMode {
JKQTPRGBMathImageModeRGBMode=0, JKQTPRGBMathImageModeRGBMode=0, /*!< image channels are mapped to the R, G and B channel (red-green-blue) */
JKQTPRGBMathImageModeHSVMode=1, JKQTPRGBMathImageModeHSVMode=1, /*!< image channels are mapped to the H, S and V channel (hue-saturation-value) */
JKQTPRGBMathImageModeHSLMode=2, JKQTPRGBMathImageModeHSLMode=2, /*!< image channels are mapped to the H, S and L channel (bue-saturation-luminance) */
JKQTPRGBMathImageModeCMYMode=3 JKQTPRGBMathImageModeCMYMode=3 /*!< image channels are mapped to the C, M and Y channel (subtractive color model!!!) */
}; };

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 KiB

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 KiB

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB