Увеличение контрастности изображения

122
09 февраля 2021, 01:30

Чтобы увеличить резкость изображения я использую следующую функцию:

int oldToNew(int oldVal, int oldMin, int oldMax, int newMin, int newMax) {
  int retval =
      ((oldVal - oldMin) * (newMax - newMin) / (oldMax - oldMin)) + newMin;
  retval = retval < 0 ? 0 : retval;
  retval = retval > 255 ? 255 : retval;
  return retval;
}

Ей я передаю значения от 0 до 255 (первое значение) и значения: 25, 212, 0, 255. Резултьтат записываю в массив, который потом использую для изменения изображения (чтобы не рассчитывать это для каждого пикселя каждый раз).

unsigned char newRange[256]{};
for (size_t i{}; i < 256; ++i) {
  newRange[i] = oldToNew(i, 25, 212, 0, 255);
}
for (size_t i{}; i < img.rows * img.cols * img.elemSize(); ++i) {
  rez.data[i] = newRange[img.data[i]];
}

В результате получается что-то вроде того:

На глаз вроде то, что было нужно, но если вывести гистограмму изображения, то получится примерно следующее:

[3 []4

1) Почему выходная гистограмма получилась такая? 2) Правильно ли я вообще делаю? 3) Как сделать тоже самое с помощью гистограмм?

Весь код:

// main.cpp
#include <cstdlib>
#include <iostream>
#include <opencv2/opencv.hpp>
int oldToNew(int oldVal, int oldMin, int oldMax, int newMin, int newMax) {
  int retval =
      ((oldVal - oldMin) * (newMax - newMin) / (oldMax - oldMin)) + newMin;
  retval = retval < 0 ? 0 : retval;
  retval = retval > 255 ? 255 : retval;
  return retval;
}
cv::Mat getHisto(const cv::Mat &image) {
  std::vector<cv::Mat> planes;
  cv::split(image, planes);
  int          histSize  = 256;
  float        range[]   = {0, 255};
  const float *histRange = range;
  std::vector<cv::Mat> hist;
  hist.resize(3);
  cv::calcHist(&planes[0],
               1,
               0,
               cv::Mat(),
               hist[0],
               1,
               &histSize,
               &histRange,
               true,
               false);
  cv::calcHist(&planes[1],
               1,
               0,
               cv::Mat(),
               hist[1],
               1,
               &histSize,
               &histRange,
               true,
               false);
  cv::calcHist(&planes[2],
               1,
               0,
               cv::Mat(),
               hist[2],
               1,
               &histSize,
               &histRange,
               true,
               false);
  int histW = 512;
  int histH = 400;
  int binW  = cvRound((double)histW / histSize);
  cv::Mat histImage(histH, histW, CV_8UC3, cv::Scalar(0, 0, 0));
  cv::normalize(
      hist[0], hist[0], 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
  cv::normalize(
      hist[1], hist[1], 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
  cv::normalize(
      hist[2], hist[2], 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
  /// Draw for each channel
  for (int i = 1; i < histSize; i++) {
    cv::line(
        histImage,
        cv::Point(binW * (i - 1), histH - cvRound(hist[0].at<float>(i - 1))),
        cv::Point(binW * (i), histH - cvRound(hist[0].at<float>(i))),
        cv::Scalar(255, 0, 0),
        2,
        8,
        0);
    cv::line(
        histImage,
        cv::Point(binW * (i - 1), histH - cvRound(hist[1].at<float>(i - 1))),
        cv::Point(binW * (i), histH - cvRound(hist[1].at<float>(i))),
        cv::Scalar(0, 255, 0),
        2,
        8,
        0);
    cv::line(
        histImage,
        cv::Point(binW * (i - 1), histH - cvRound(hist[2].at<float>(i - 1))),
        cv::Point(binW * (i), histH - cvRound(hist[2].at<float>(i))),
        cv::Scalar(0, 0, 255),
        2,
        8,
        0);
  }
  return histImage;
}
int main(int argc, char *argv[]) {
  if (argc < 2) {
    std::cerr << "you need input image" << std::endl;
    return EXIT_FAILURE;
  }
  cv::Mat       img = cv::imread(argv[1], -1);
  cv::Mat       rez{img.size(), img.type()};
  unsigned char newRange[256]{};
  for (size_t i{}; i < 256; ++i) {
    newRange[i] = oldToNew(i, 25, 212, 0, 255);
  }
  for (size_t i{}; i < img.rows * img.cols * img.elemSize(); ++i) {
    rez.data[i] = newRange[img.data[i]];
  }
  cv::imshow("input", img);
  cv::imshow("output", rez);
  auto inHisto  = getHisto(img);
  auto outHisto = getHisto(rez);
  cv::imshow("in histo", inHisto);
  cv::imshow("out histo", outHisto);
  cv::waitKey(0);
  cv::destroyAllWindows();
  cv::imwrite("1.jpg", rez);
  cv::imwrite("inHisto.jpg", inHisto);
  cv::imwrite("outHisto.jpg", outHisto);
  return EXIT_SUCCESS;
}
READ ALSO
В с++ getline дает нечто странное

В с++ getline дает нечто странное

Получаю поток std::istream, считываю строку методом getline(stream, line)

89
C++ Считать строку из n цифр или всю строку с пробелами

C++ Считать строку из n цифр или всю строку с пробелами

Пользователь вводит с клавиатуры символыЕсли он ввёл n-цифр подряд (не нажимая "Enter"), то завершить ввод и вернуть эту строку

95
Как оператором = вернуть значение

Как оператором = вернуть значение

Есть задание разработать класс Triangle и в нем перегрузить оператор =, который возвращал бы площадь этого треугольникаВопрос в том, как это сделать?...

107