Нахожу в тесте вопросы, которые в принципе не освещаются в лекции. Нужно гуглить на других ресурсах, чтобы решить тест, или же он всё же должен испытывать знания, полученные в ходе лекции? |
Базовые операции обработки изображений
2.6. Детектор ребер Канни
Детектор ребер Канни [4, 8, 9] предназначен для поиска границ объектов на изображении. Детектор строится на основании оператора Собеля и включает несколько этапов:
- Удаление шума на изображении посредством применения фильтра Гаусса с ядром размера 5:
- Вычисление первых производных (магнитуд и направлений) функции интенсивности пикселей по горизонтальному и вертикальному направлениям посредством применения оператора Собеля с ядрами и (см. раздел 2.4). Направления градиентов округляются до одного из возможных значений ,,,.
- Отбор пикселей, которые потенциально принадлежат ребру с использованием процедуры non-maximum suppression [4]. Пиксели, которым соответствуют вектора производных по направлениям, являющиеся локальными максимумами, считаются потенциальными кандидатами на принадлежность ребру.
- Двойное отсечение (гистерезис). Выделяются "сильные" и "слабые" ребра. Пиксели, интенсивность которых превышает максимальный порог, считаются пикселями, принадлежащими "сильным" ребрам. Принимается, что пиксели с интенсивностью, входящей в интервал от минимального до максимального порогового значения, принадлежат "слабым" ребрам. Пиксели, интенсивность которых меньше минимального порога, отбрасываются из дальнейшего рассмотрения. Результирующие ребра содержат пиксели всех "сильных" ребер и те пиксели "слабых" ребер, чья окрестность содержит хотя бы один пиксель "сильных" ребер.
Детектор Канни реализован в библиотеке OpenCV [7] в виде отдельной функции, прототип которой приведен далее.
void Canny(const Mat&image, Mat&edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)
Функция принимает на вход следующие параметры:
- image – одноканальное 8-битное изображение.
- edges – результирующая карта ребер, представляется матрицей, размер которой совпадает с размером исходного изображения.
- threshold1, threshold2 – параметры алгоритма, пороговые значения для отсечения.
- apertureSize – размер апертуры для применения оператора Собеля.
- L2gradient – флаг, который указывает, по какой норме будет вычисляться магнитуда градиента. Принимает истинное значение, если используется норма (корень квадратный из суммы квадратов частных производных), в противном случае (сумма модулей частных производных). Как правило, нормы достаточно, и вычисляется она быстрее в связи с отсутствием вызова функции sqrt.
Приведем пример использования детектора Канни. Отметим, что перед непосредственным применением детектора выполняется размытие изображения (blur) и преобразование в оттенки серого (cvtColor).
#include <stdio.h> #include <opencv2/opencv.hpp> using namespace cv; const char helper[] = "Sample_Canny.exe <img_file>\n\ \t<img_file> - image file name\n"; int main(int argc, char* argv[]) { const char *cannyWinName = "Canny detector"; Mat img, grayImg, edgesImg; double lowThreshold = 70, uppThreshold = 260; if (argc < 2) { printf("%s", helper); return 1; } // загрузка изображения img = imread(argv[1], 1); // удаление шумов blur(img, img, Size(3,3)); // преобразование в оттенки серого cvtColor(img, grayImg, CV_RGB2GRAY); // применение детектора Канни Canny(grayImg, edgesImg, lowThreshold, uppThreshold); // отображение результата namedWindow(cannyWinName, CV_WINDOW_AUTOSIZE); imshow(cannyWinName, edgesImg); waitKey(); // закрытие окон destroyAllWindows(); // осовобождение памяти img.release(); grayImg.release(); edgesImg.release(); return 0; }
На рисунке (рис.9.9) показан результат применения детектора Канни к тестовому изображению (рис.9.2, слева).
2.7. Вычисление гистограмм
Один из наиболее распространенных дефектов фотографических, сканерных и телевизионных изображений – слабый контраст. Дефект во многом обусловлен ограниченностью диапазона воспроизводимых яркостей. Под контрастом понимается разность максимального и минимального значений яркости. Контрастность изображения можно повысить за счет изменения яркости каждого элемента изображения и увеличения диапазона яркостей. Существует несколько методов, основанных на вычислении гистограммы.
Допустим, что имеется изображение в оттенках серого, интенсивность пикселей которого изменяется в пределах значений от a до b , где и . Для изображения можно построить гистограмму со столбцами, отвечающими количеству пикселей определенной интенсивности. Такого рода гистограмма позволяет представить распределение оттенков на изображении. В общем случае под гистограммой понимается коллекция целочисленных значений, каждое из которых определяет количество точек, обладающих некоторым свойством или принадлежащих определенному бину. На практике гистограммы применяются, чтобы получить статистическую картину о распределении каких-либо данных (пикселей, векторов признаков, направлений градиента во всех точках изображения и т.п.).
В данном разделе остановимся на рассмотрении структур данных и функций 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 показан на рисунке (рис.9.10) ниже.