21. OpenCV шаг за шагом. Обработка изображения — пороговое преобразование


1. OpenCV шаг за шагом. Введение.
2. Установка.
3. Hello World.
4. Загрузка картинки.
5. Вывод видео
6. Ползунок
7. Захват видео с камеры
8. Запись видео
9. События от мышки
10. Обработка изображения — сглаживание
11. Обработка изображения — изменение размеров
12. ROI — интересующая область изображения
13. Типы данных OpenCV
14. Матрица
15. Сохранение данных в XML
16. Генерация случайных чисел
17. Обработка изображения — морфологические преобразования
18. Обработка изображения — морфологические преобразования 2
19. Обработка изображения — заливка части изображения
20. Обработка изображения — альфа-смешивание
21. Обработка изображения — пороговое преобразование

Как помним, изображение — это массив пикселей. Нетрудно придумать задачи, когда будет требоваться выбрать пиксели выше(ниже, между) определённого порогового значения.
И тут нам на выручку приходит функция cvThreshold()

CVAPI(double)  cvThreshold( const CvArr*  src, CvArr*  dst,
                            double  threshold, double  max_value,
                            int threshold_type );

— выполняет фиксированное пороговое преобразование для элементов массива.

src — исходный массив(изображение) (одноканальное, 8-битное или 32-битное)
dst — целевой массив, должен иметь тот же тип что и src или 8-битный
threshold — пороговая величина
max_value — максимальное значение (используется совместно с CV_THRESH_BINARY и CV_THRESH_BINARY_INV)
threshold_type — тип порогового преобразования:

#define CV_THRESH_BINARY      0  /* value = value > threshold ? max_value : 0       */
#define CV_THRESH_BINARY_INV  1  /* value = value > threshold ? 0 : max_value       */
#define CV_THRESH_TRUNC       2  /* value = value > threshold ? threshold : value   */
#define CV_THRESH_TOZERO      3  /* value = value > threshold ? value : 0           */
#define CV_THRESH_TOZERO_INV  4  /* value = value > threshold ? 0 : value           */
#define CV_THRESH_MASK        7
#define CV_THRESH_OTSU        8  /* use Otsu algorithm to choose the optimal threshold value;
                                    combine the flag with one of the above CV_THRESH_* values */


обычное пороговое преобразование никак не учитывает, что части объектов могут иметь различную яркость из-за разности в освещённости. Это можно исправить если использовать адаптивное пороговое преобразование, которое рассматривает значение не в одном пикселе, а в окрестности пикселя. Это значение может быть просто средним значением пикселей окрестности(т.е. все пиксели равнозначны)(CV_ADAPTIVE_THRESH_MEAN_C), либо пиксели окрестности умножаются на весовой коэффициент (взвешиваются) в соответствии с функцией, например с гауссовой функцией (CV_ADAPTIVE_THRESH_GAUSSIAN_C).

CVAPI(void)  cvAdaptiveThreshold( const CvArr* src, CvArr* dst, double max_value,
                                  int adaptive_method CV_DEFAULT(CV_ADAPTIVE_THRESH_MEAN_C),
                                  int threshold_type CV_DEFAULT(CV_THRESH_BINARY),
                                  int block_size CV_DEFAULT(3),
                                  double param1 CV_DEFAULT(5));

— выполняет адаптивное пороговое преобразование для элементов массива.

src — исходное изображение
dst — целевое изображение
max_value — максимальное значение (используется совместно с CV_THRESH_BINARY и CV_THRESH_BINARY_INV)
adaptive_method — используемый адаптационный алгоритм:

#define CV_ADAPTIVE_THRESH_MEAN_C  0
#define CV_ADAPTIVE_THRESH_GAUSSIAN_C  1

threshold_type — тип порогового преобразования:

#define CV_THRESH_BINARY      0  /* value = value > threshold ? max_value : 0       */
#define CV_THRESH_BINARY_INV  1  /* value = value > threshold ? 0 : max_value       */

block_size — размер окрестности (в пикселях), которая используется для расчёта порогового значения: 3, 5, 7 и т.д.
param1 — параметр, зависящий от используемого метода. Для методов CV_ADAPTIVE_THRESH_MEAN_C и CV_ADAPTIVE_THRESH_GAUSSIAN_C — это константа, вычитаемая из среднего или взвешенного значения (может быть отрицательной)

//
// пример порогового преобразования
// cvThreshold() и cvAdaptiveThreshold()
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
	IplImage *src=0, *dst=0, *dst2=0;

	// имя картинки задаётся первым параметром
	char* filename = argc >= 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	src = cvLoadImage(filename, 0);

	printf("[i] image: %s\n", filename);
	assert( src != 0 );

	// покажем изображение
	cvNamedWindow( "original", 1 );
	cvShowImage( "original", src );

	dst = cvCreateImage( cvSize(src->width, src->height), IPL_DEPTH_8U, 1);
	dst2 = cvCreateImage( cvSize(src->width, src->height), IPL_DEPTH_8U, 1);

	cvThreshold(src, dst, 50, 250, CV_THRESH_BINARY);
	cvAdaptiveThreshold(src, dst2, 250, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, 1);

	// показываем результаты
	cvNamedWindow( "cvThreshold", 1 );
	cvShowImage( "cvThreshold", dst);
	cvNamedWindow( "cvAdaptiveThreshold", 1 );
	cvShowImage( "cvAdaptiveThreshold", dst2);

	// ждём нажатия клавиши
	cvWaitKey(0);

	// освобождаем ресурсы
	cvReleaseImage(&src);
	cvReleaseImage(&dst);
	cvReleaseImage(&dst2);
	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

Аналогичный результат можно получить, используя функции для выборки тех пикселей изображения, которые лежат в заданном интервале значений ( могут храниться как в массиве — cvInRange(), так и задаваться скалярами cvInRangeS() )

CVAPI(void) cvInRange( const CvArr* src, const CvArr* lower,
                      const CvArr* upper, CvArr* dst );

— по-элементная проверка элементов массива: проверяет, что значения элементов массива лежат между значениями элементов двух других массивов.
формула:

	dst(idx) = lower(idx) <= src(idx) < upper(idx)

src - исходный массив
lower - массив с нижней границей (включая)
upper - массив с верхней границей (не включая)
dst - массив для хранения результата (тип 8S или 8U)

CVAPI(void) cvInRangeS( const CvArr* src, CvScalar lower,
                       CvScalar upper, CvArr* dst );

- проверяет, что элемент массива лежит между двух скаляров
формула:

	dst(idx) = lower <= src(idx) < upper

src - исходный массив
lower - скаляр с нижней границей (включая)
upper - скаляр с верхней границей (не включая)
dst - массив для хранения результата (тип 8S или 8U)

Пример использования:

//
// пример выборки значений в заданном интервале
// cvInRangeS()
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
	IplImage *src=0, *dst=0;

	// имя картинки задаётся первым параметром
	char* filename = argc >= 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	src = cvLoadImage(filename, 0);

	printf("[i] image: %s\n", filename);
	assert( src != 0 );

	// покажем изображение
	cvNamedWindow( "original", 1 );
	cvShowImage( "original", src );

	dst = cvCreateImage( cvSize(src->width, src->height), IPL_DEPTH_8U, 1);

	cvInRangeS(src, cvScalar(50), cvScalar(255), dst);

	// показываем результаты
	cvNamedWindow( "cvInRangeS", 1 );
	cvShowImage( "cvInRangeS", dst);

	// ждём нажатия клавиши
	cvWaitKey(0);

	// освобождаем ресурсы
	cvReleaseImage(&src);
	cvReleaseImage(&dst);
	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}


Простой пример практического применения этих функций - выборка определённых областей изображения, которые отличаются от других яркостью (в случае изображения 1-канального изображения (в градациях серого) ) или цветом (в случае цветного изображения).

дополнительно:
Вопросы OpenCV - пороговое преобразование над цветной картинкой

Читать далее: 22. Поиск объекта по цвету - RGB.


0 комментариев на «“21. OpenCV шаг за шагом. Обработка изображения — пороговое преобразование”»

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение