Here I will show you how to plot a multi-channel/colour histogram using QWT. I generate the histograms for the different channels in a RGB image using OpenCV, then I plot the histogram of each channel overlaid on each other on the same axis. This method is suitable for visualization but not very suitable for peak/valley histogram analysis as the overlaid colours would merge making it difficult to see what peaks/valleys belong to which channel.
The set-up for this tutorial is similar to the one in my previous tutorial, only this time I also include the OpenCV library. Again, we can just dive straight into the code.
Code
#include <QApplication> #include <qwt_plot.h> #include <qwt_plot_curve.h> #include <qwt_plot_grid.h> #include <qwt_symbol.h> #include <qwt_legend.h> #include <opencv2/core/core.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> using namespace cv; void getPoints(MatND& hist, int* histSize, QPolygonF& points) { for( int h = 0; h < histSize[0]; ++h) { float bin_value = hist.at<float>(h); points << QPointF((float)h, bin_value); } } class Curve: public QwtPlotCurve { public: Curve( const QString &title ): QwtPlotCurve( title ) { setRenderHint( QwtPlotItem::RenderAntialiased ); } void setColour(const QColor &color, int penSize) { QColor c = color; c.setAlpha( 150 ); setPen(c, penSize); setBrush( c ); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); if (argc < 2) return 1; //Read input image Mat img = cv::imread(argv[1]); //Convert to grayscale if (img.data && img.channels() != 3) return 1; QwtPlot plot; //Create plot widget plot.setTitle( "Plot Demo" ); //Name the plot plot.setCanvasBackground(Qt::white ); //Set the Background colour plot.setAxisScale(QwtPlot::xBottom,0,255); //Scale the x-axis plot.insertLegend(new QwtLegend()); //Insert a legend int histSize[] = {256}; // number of bins float hranges[] = {0.0, 255.0}; // min and max pixel value const float* ranges[] = {hranges}; int channels[] = {0}; // only 1 channel used std::vector<cv::Mat> rgbChannels(3); split(img, rgbChannels); MatND hist; QPolygonF points; calcHist(&rgbChannels[2], 1, channels, cv::Mat(), hist, 1, histSize,ranges); Curve *curve = new Curve("Red Channel"); curve->setColour(Qt::red , 2);//Set colour and thickness for drawn curve. /*Insert the points that should be plotted on the graph in a Vector of QPoints or a QPolgonF */ getPoints(hist, histSize, points); curve->setZ( curve->z() - 1 ); curve->setSamples(points); //pass points to be drawn on the curve curve->attach( &plot ); // Attach curve to the plot calcHist(&rgbChannels[1], 1, channels, cv::Mat(), hist, 1, histSize,ranges); curve = new Curve("Green Channel"); curve->setColour(Qt::green , 2); points.clear(); getPoints(hist, histSize, points); curve->setZ( curve->z() - 2 ); curve->setSamples( points ); curve->attach( &plot ); calcHist(&rgbChannels[0], 1, channels, cv::Mat(), hist, 1, histSize,ranges); curve = new Curve("Blue Channel"); curve->setColour(Qt::blue, 2); points.clear(); getPoints(hist, histSize, points); curve->setZ( curve->z() - 3 ); curve->setSamples( points ); curve->attach( &plot ); plot.resize( 600, 400 ); //Resize the plot plot.show(); //Show plot return a.exec(); }
Result
Figure 1: A) The input image B) The resulting plot
Note: It is possible to remove the coloured area under the curves and reduce the plot to a simple line plot. This would make each channels' peaks and valleys more visible. This could be done by commenting out line:36 in the code. The result is shown below.
Figure 2: The result with the setBrush() property turned off.
Conclusion
That concludes this part of the tutorial, it is also possible to plot each histogram in its separate axis (in a matrix format) similar to what using the subplot() function in matlab does. This would be seen in our next tutorial. Happy Coding!.
In order to get this to compile under qwt 6.0.1, I had to change line 35 to
ReplyDeletesetPen(QPen(c, penSize));