Во время работы над программой, предназначенной для обработки видео-потока, возникла необходимость реализовать алгоритм изменения контраста изображения.
Так как программа была предназначена для обработки видео, то от реализации требовалась высокая производительность, в том числе способность обрабатывать видео разрешения Full HD. Код был написан на С++ с использованием библиотеки OpenMP.
Существует несколько алгоритмов изменения контраста, часть из которых рассмотрена в этой статье [1].
Рассмотрим RGB-алгоритм изменения контраста.
Вначале мы вычисляем среднее значение яркости изображения lAB. Используем директиву OpenMP для распараллеливания цикла:
#pragma omp parallel for private(valueB,valueG,valueR) reduction(+:lAB) schedule(static,1)
for (int j = 0; j < ptrImage->imageSize; j = j+3)
{
valueB = ptrImage->imageData[j];
valueG = ptrImage->imageData[j+1];
valueR = ptrImage->imageData[j+2];
lAB += (int)(valueR * 0.299 + valueG * 0.587 + valueB * 0.114);
}
//средняя яркость
lAB /= imageRows * imageCols;
Затем для каждого пикселя изображения для каждой из цветовых компонент R, G, и B мы должны найти отклонение от среднего значения яркости delta, и это отклонение умножить на коэффициент усиления(ослабления) контраста k. Выполнять это непосредственно над всеми пикселями изображения слишком дорогостоящая операция. Поэтому, учитывая, что мы преобразовываем все цветовые компоненты пикселя одинаково, введём массив из 256 значений, который будет представлять палитру для каждой цветовой компоненты. Индекс массива соответствует старому значению цветовой компоненты, а значение массива — преобразованному. Таким образом, применяя ранее описанные преобразования для изменения контраста к палитре, получим:
//Коэффициент коррекции
double k = 1.0 + correction / 100.0;
for (int i = 0; i < 256; i++)
{
int delta = (int)i - lAB;
int temp = (int)(lAB + k *delta);
if (temp < 0)
temp = 0;
if (temp >= 255)
temp = 255;
b[i] = (unsigned char)temp;
}
И далее, применяем новые значения для пикселей всего изображения, сопоставляя старое значение цветовой компоненты пикселя с новым, используя полученный массив палитры:
#pragma omp parallel for firstprivate(b)
for (int j = 0; j < ptrImage->imageSize; j++)
{
unsigned char value = ptrImage->imageData[j];
ptrImage->imageData[j] = (unsigned char)b[value];
}
Весь исходный код функции для изменения контраста:
const int L = 256;
void AddContrastFilter(IplImage* ptrImage, int _correction)
{
// установить число потоков в 4, равным количеству процессоров в системе
omp_set_num_threads(4);
int correction = _correction;
//палитра
int b[L];
//Число строк и столбцов
int imageRows = ptrImage->height;
int imageCols = ptrImage->width;
int lAB = 0;
unsigned char valueB = 0;
unsigned char valueG= 0;
unsigned char valueR= 0;
//Находим яркость всех пикселей
#pragma omp parallel for private(valueB,valueG,valueR) reduction(+:lAB) schedule(static,1)
for (int j = 0; j < ptrImage->imageSize; j = j+3)
{
valueB = ptrImage->imageData[j];
valueG = ptrImage->imageData[j+1];
valueR = ptrImage->imageData[j+2];
lAB += (int)(valueR * 0.299 + valueG * 0.587 + valueB * 0.114);
}
//средняя яркость всех пикселей
lAB /= imageRows * imageCols;
//Коэффициент коррекции
double k = 1.0 + correction / 100.0;
//RGB алгоритм изменения контраста
for (int i = 0; i < L; i++)
{
int delta = (int)i - lAB;
int temp = (int)(lAB + k *delta);
if (temp < 0)
temp = 0;
if (temp >= 255)
temp = 255;
b[i] = (unsigned char)temp;
}
#pragma omp parallel for firstprivate(b)
for (int j = 0; j < ptrImage->imageSize; j++)
{
unsigned char value = ptrImage->imageData[j];
ptrImage->imageData[j] = (unsigned char)b[value];
}
return 0;
}
У меня получилось, что изменение контраста Full HD кадра на четырёх-ядерном процессоре Intel Xeon занимает по времени порядка 8 мс.
Ссылки
1. Преобразование контраста в программном обеспечении: http://www.kweii.com/site/color_theory/Contrast100_ru.pdf