#include "histogram.hpp" #include <QHBoxLayout> #include <iostream> #include "util.hpp" namespace cvv { namespace qtutil { Histogram::Histogram(const cv::Mat& mat, QWidget* parent) :QWidget{parent}, histSize_(512, 200), histLineWidth_(2), histBackgroundColor_(255, 255, 255) { setMat(mat); zoomableImage = new ZoomableImage(); auto layout = new QHBoxLayout(); layout->addWidget(zoomableImage); setLayout(layout); } void Histogram::setMat(const cv::Mat& mat) { mat_ = mat; } cv::Rect Histogram::qrect2cvrect(const cv::Mat& mat, QRectF qrect) { double x1, y1, x2, y2; qrect.getCoords(&x1, &y1, &x2, &y2); x1 = std::max(0.0, x1); y1 = std::max(0.0, y1); x2 = std::min(static_cast<double>(mat.size().width), x2); y2 = std::min(static_cast<double>(mat.size().height), y2); double width = x2 - x1; double height = y2 - y1; return cv::Rect(x1, y1, width, height); } void Histogram::setArea(QRectF rect, qreal zoom) { (void)zoom; channelHists_ = calcHist(mat_, qrect2cvrect(mat_, rect)); histMat_ = drawHist(channelHists_, histSize_, histLineWidth_, histBackgroundColor_); zoomableImage->setMat(histMat_); zoomableImage->showFullImage(); } std::vector<cv::Mat> Histogram::calcHist(cv::Mat mat, cv::Rect rect, int bins, float rangeMin, float rangeMax) { cv::Mat rectMat(mat, rect); cv::Mat histMat; std::vector<cv::Mat> channelPlanes = splitChannels(rectMat); int histSize = bins; float range[] = {rangeMin, rangeMax}; const float* histRange = {range}; bool uniform = true; bool accumulate = false; std::vector<cv::Mat> channelHists(channelPlanes.size()); for (size_t chan = 0; chan < channelPlanes.size(); chan++) { cv::calcHist(&channelPlanes[chan], 1, 0, cv::Mat(), channelHists[chan], 1, &histSize, &histRange, uniform, accumulate); } return channelHists; } cv::Mat Histogram::drawHist(const std::vector<cv::Mat>& channelHists, cv::Size histSize, int lineWidth, const cv::Scalar& backgroundColor) { int binCount = channelHists[0].rows; int binWidth = cvRound(double(histSize.width)/binCount); std::vector<cv::Scalar> colors{cv::Scalar(255, 0, 0), cv::Scalar(0, 255, 0), cv::Scalar(0, 0, 255), cv::Scalar(0, 0, 0)}; // BGR cv::Mat histMat(histSize, CV_8UC3, backgroundColor); double maxVal = 0; for (auto& hist : channelHists) { double tmpMaxVal; cv::minMaxLoc(hist, NULL, &tmpMaxVal); maxVal = std::max(maxVal, tmpMaxVal); } double valScale = histSize.height / maxVal; for (size_t channel = 0; channel < channelHists.size(); channel++) { auto& hist = channelHists[channel]; auto& color = colors[channel]; for (int bin = 1; bin < binCount; bin++) { //printf("%zd:%d=%f\n", channel, bin, hist.at<float>(bin)); auto pt1 = cv::Point(binWidth * (bin-1), histSize.height - cvRound(hist.at<float>(bin-1) * valScale)); auto pt2 = cv::Point(binWidth * bin, histSize.height - cvRound(hist.at<float>(bin) * valScale)); cv::line(histMat, pt1, pt2, color, lineWidth); } } int binTextStep = binCount / 5; binTextStep = binTextStep - (binTextStep % 10); // round to tens int fontFace = cv::FONT_HERSHEY_SCRIPT_SIMPLEX; double fontScale = 0.5; auto textColor = cv::Scalar::all(0); int thickness = 1; for (int binTextId = 0; binTextId < binCount; binTextId += binTextStep) { auto text = QString::number(binTextId).toLatin1(); auto textSize = cv::getTextSize(text.data(), fontFace, fontScale, thickness, NULL); auto textPt = cv::Point(std::max(0, binWidth * binTextId - textSize.width/2), histSize.height); cv::putText(histMat, text.data(), textPt, fontFace, fontScale, textColor, thickness); auto linePt1 = cv::Point(binWidth * binTextId, 0); auto linePt2 = cv::Point(binWidth * binTextId, histSize.height - textSize.height); cv::line(histMat, linePt1, linePt2, textColor); } return histMat; } } }