Опубликован: 20.08.2013 | Уровень: для всех | Доступ: платный | ВУЗ: Новосибирский Государственный Университет
Самостоятельная работа 2:

Базовые операции обработки изображений

2.7. Вычисление гистограмм

Один из наиболее распространенных дефектов фотографических, сканерных и телевизионных изображений – слабый контраст. Дефект во многом обусловлен ограниченностью диапазона воспроизводимых яркостей. Под контрастом понимается разность максимального и минимального значений яркости. Контрастность изображения можно повысить за счет изменения яркости каждого элемента изображения и увеличения диапазона яркостей. Существует несколько методов, основанных на вычислении гистограммы.

Допустим, что имеется изображение в оттенках серого, интенсивность пикселей которого изменяется в пределах значений от a до b, где a \geq 0 и b \leq 255. Для изображения можно построить гистограмму со столбцами, отвечающими количеству пикселей определенной интенсивности. Такого рода гистограмма позволяет представить распределение оттенков на изображении. В общем случае под гистограммой понимается коллекция целочисленных значений, каждое из которых определяет количество точек, обладающих некоторым свойством или принадлежащих определенному бину. На практике гистограммы применяются, чтобы получить статистическую картину о распределении каких-либо данных (пикселей, векторов признаков, направлений градиента во всех точках изображения и т.п.).

В данном разделе остановимся на рассмотрении структур данных и функций OpenCV, обеспечивающих вычисление гистограмм. Ниже приведены прототипы доступных функций.

void calcHist(const Mat* arrays, int narrays, 
    const int* channels, const Mat& mask, 
    MatND& hist, int dims, const int* histSize, 
    const float** ranges, bool uniform=true, 
    bool accumulate=false) 
 
void calcHist(const Mat* arrays, int narrays, 
    const int* channels, const Mat& mask, 
    SparseMat& hist, int dims, 
    const int* histSize, const float** ranges, 
    bool uniform=true, bool accumulate=false) 
    

Параметры:

  • arrays – исходные массивы данных или изображения. Должны иметь одинаковую глубину (CV_8U или CV_32F) и размер.
  • narrays – количество исходных массивов данных.
  • channels – массив индексов каналов в каждом входном массиве, по которым будет вычисляться гистограмма.
  • mask – маска, на которой считается гистограмма. Опциональный параметр. Если маска не пуста, то она представляется 8-битной матрицей того же размера, что и каждый исходный массив. При построении гистограммы учитываются только элементы массивов, которые соответствуют ненулевым элементам маски. Если маска пуста, то построение гистограммы выполняется на полном наборе данных.
  • hist – результирующая гистограмма, плотная в случае использования первого прототипа функции, разреженная – в случае второго. Для хранения плотной гистограммы используется структура данных MatND, для разреженной – SparseMat. MatND представляется в виде n-мерного массива, SparseMat – хэш-таблицей ненулевых значений [10].
  • dims – размерность гистограммы. Параметр принимает положительные целочисленные значения, не превышающие CV_MAX_DIMS = 32.
  • histSize – количество бинов по каждой размерности гистограммы.
  • ranges – интервалы изменения значений по каждой размерности гистограммы. Если гистограмма равномерная (uniform = true), то для любой размерности i достаточно указать только нижнюю границу изменения (по существу значение, соответствующее первому бину), верхняя граница будет совпадать с histSize[i]-1.
  • uniform – флаг, который определяет тип диаграммы (равномерная или нет).
  • accumulate – флаг, указывающий на необходимость очищения гистограммы перед непосредственными вычислениями. Использование данного флага позволяет использовать одну и ту же гистограмму для нескольких множеств массивов или обновлять гистограмму во времени.

Рассмотрим пример программы, которая осуществляет построение и отображение гистограмм по каждому каналу цветного изображения. Программа получает в качестве аргументов командной строки название изображения, расщепляет полученную матрицу по каналам (split) и вычисляет гистограмму для каждого канала изображения (calcHist). Заметим, что в OpenCV каналы изображения хранятся в порядке BGR, а не в RGB. Далее выполняется нормализация гистограмм (normalize) для приемлемого отображения в виде ломаных.

#include <stdio.h> 
#include <opencv2/opencv.hpp> 
 
using namespace cv; 
 
const char helper[] = 
  "Sample_calcHist.exe <img_file>\n\ 
  \t<img_file> - image file name\n"; 
 
int main(int argc, char* argv[]) 
{ 
  const char *initialWinName = "Initial Image", 
    *histWinName = "Histogram"; 
  Mat img, bgrChannels[3], bHist, gHist, rHist, histImg; 
  int kBins = 256; // количество бинов гистограммы 
  // интервал изменения значений бинов 
  float range[] = {0.0f, 256.0f}; 
  const float* histRange = { range }; 
  // равномерное распределение интервала по бинам 
  bool uniform = true; 
  // запрет очищения перед вычислением гистограммы 
  bool accumulate = false; 
  // размеры для отображения гистограммы 
  int histWidth = 512, histHeight = 400; 
  // количество пикселей на бин 
  int binWidth = cvRound((double)histWidth / kBins); 
  int i, kChannels = 3; 
  Scalar colors[] = {Scalar(255, 0, 0), 
      Scalar(0, 255, 0), Scalar(0, 0, 255)}; 
  if (argc < 2) 
  { 
    printf("%s", helper); 
    return 1; 
  } 
  // загрузка изображения 
  img = imread(argv[1], 1); 
  // выделение каналов изображения 
  split(img, bgrChannels); 
  // вычисление гистограммы для каждого канала 
  calcHist("bgrChannels[0], 1, 0, Mat(), bHist, 1, 
    "kBins, "histRange, uniform, accumulate); 
  calcHist("bgrChannels[1], 1, 0, Mat(), gHist, 1, 
    "kBins, "histRange, uniform, accumulate); 
  calcHist("bgrChannels[2], 1, 0, Mat(), rHist, 1, 
    "kBins, "histRange, uniform, accumulate); 
 
  // построение гистограммы 
  histImg = Mat(histHeight, histWidth, CV_8UC3, 
      Scalar(0, 0, 0)); 
  // нормализация гистограмм в соответствии с размерам
  // окна для отображения 
  normalize(bHist, bHist, 0, histImg.rows, 
    NORM_MINMAX, -1, Mat()); 
  normalize(gHist, gHist, 0, histImg.rows, 
    NORM_MINMAX, -1, Mat()); 
  normalize(rHist, rHist, 0, histImg.rows, 
    NORM_MINMAX, -1, Mat()); 
  // отрисовка ломаных 
  for (i = 1; i < kBins; i++) 
  { 
    line(histImg, Point(binWidth * (i-1), 
        histHeight-cvRound(bHist.at<float>(i-1))) , 
      Point(binWidth * i, 
        histHeight-cvRound(bHist.at<float>(i)) ), 
      colors[0], 2, 8, 0); 
    line(histImg, Point(binWidth * (i-1), 
        histHeight-cvRound(gHist.at<float>(i-1))) , 
      Point(binWidth * i, 
        histHeight-cvRound(gHist.at<float>(i)) ), 
      colors[1], 2, 8, 0); 
    line(histImg, Point(binWidth * (i-1), 
        histHeight-cvRound(rHist.at<float>(i-1))) , 
      Point(binWidth * i, 
        histHeight-cvRound(rHist.at<float>(i)) ), 
      colors[2], 2, 8, 0); 
  } 
  // отображение исходного изображения и гистограмм 
  namedWindow(initialWinName, CV_WINDOW_AUTOSIZE); 
  namedWindow(histWinName, CV_WINDOW_AUTOSIZE); 
  imshow(initialWinName, img); 
  imshow(histWinName, histImg);
  waitKey(); 
 
  // закрытие окон 
  destroyAllWindows(); 
  // осовобождение памяти 
  img.release(); 
  for (i = 0; i < kChannels; i++) 
  { 
    bgrChannels[i].release(); 
  } 
  bHist.release(); 
  gHist.release(); 
  rHist.release(); 
  histImg.release(); 
  return 0; 
}   
    

Результат запуска программы на тестовом изображении из набора PASCAL VOC 2007 показан на рисунке (рис. 7.10) ниже.

Гистограммы распределения интенсивностей по каждому каналу исходного изображения

Рис. 7.10. Гистограммы распределения интенсивностей по каждому каналу исходного изображения
Александра Максимова
Александра Максимова

При прохождении теста 1 в нем оказались вопросы, который во-первых в 1 лекции не рассматривались, во-вторых, оказалось, что вопрос был рассмаотрен в самостоятельно работе №2. Это значит, что их нужно выполнить перед прохождением теста? или это ошибка?
 

Алена Борисова
Алена Борисова

В лекции по обработке полутоновых изображений (http://www.intuit.ru/studies/courses/10621/1105/lecture/17979?page=2) увидела следующий фильтр:


    \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 2 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array} - \frac{1}{9} \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 1 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array}

В описании говорится, что он "делает изображение более чётким, потому что, как видно из конструкции фильтра, в однородных частях изображение не изменяется, а в местах изменения яркости это изменение усиливается".

Что вижу я в конструкции фильтра (скорее всего ошибочно): F(x, y) = 2 * I(x, y) - 1/9 I(x, y) = 17/9 * I(x, y), где F(x, y) - яркость отфильтрованного пикселя, а I(x, y) - яркость исходного пикселя с координатами (x, y). Что означает обычное повышение яркости изображения, при этом без учета соседних пикселей (так как их множители равны 0).

Объясните, пожалуйста, как данный фильтр может повышать четкость изображения?

Сергей Кротов
Сергей Кротов
Россия
Дмитрий Донсков
Дмитрий Донсков
Россия, Москва, Московский Авиационный Институт