Алгоритмы обработки видео на процессоре TI DM368, продолжение…
В первой части статьи мы рассмотрели аппаратные блоки, составляющие видеопроцессор TI DM368, a сейчас переходим к обзору наших алгоритмов и коду.
Весь аппаратный конвейер камеры работает в потоковом режиме, то есть сразу после обработки пикселя одним из блоков, он передается в следующий. Программное обеспечение видеопроцессора при включении устанавливает параметры и инициализирует все конвейеры, затем входит в непрерывный цикл обработки каждого фрейма. Если частота кадров сенсора 30 fps, то все необходимые функции будут вызываться 30 раз в секунду.Теперь о принципах работы алгоритмов. Суть алгоритм AE (auto expouse — автоэкспозиции) заключается в установке значений экспозиции, коэффициента умножения (gain) и сдвига (offset) так, чтобы на выходе получить картинку с минимальным количеством пересвеченных и затемненных участков.
Экспозиция или электронный затвор отвечает за количество света, попадающее на сенсор. В современных сенсорах она может меняется от 1000000 до 1 микросекунды, поэтому отпадает необходимость использования диафрагмы для ограничения световой интенсивности. Gain и offset служат для преобразования из 12 бит в 8, и выбираются так, чтобы максимально использовать скудный 8 битовый динамический диапазон.Как правильно определить значение экспозиции? Мы пробовали несколько вариантов: и ограничивали сверху, то есть меняли экспозицию так, чтобы максимальное значение гистограммы было ниже некого порога, и опускали нижнюю границу гистограммы до определенного значения.
И тот, и другой способ максимально использует весь динамический диапазон, но имеют один недостаток — в яркий солнечный день объекты, находящиеся в тени, практически не видны. Таким образом, чтобы рассмотреть затемнения, иногда лучше пересветить некоторые участки сцены. По-этому мы отпустили максимум гистограммы в свободно плавание и экспозицию подбираем так, что бы среднее значение Y совпадало с подобранным нами значением.
Пора переходить к алгоритмам, начнем с автоэкспозиции:
1. Работа алгоритма начинается с получения данных статистики из Boxcar.
Uint32 w, h; Uint16 *box;
status = DRV_ipipeGetBoxcarBuf (&bufId, 0); if (status!= OSA_SOK) { OSA_ERROR («ERROR: DRV_ipipeGetBoxcarBuf ()\n»); return status; } pBufInfo = DRV_ipipeGetBoxcarBufInfo (bufId); DRV_ipipePutBoxcarBuf (bufId);
box = pBufInfo→virtAddr; w = gDRV_ipipeObj.boxcarInfo.width; //Boxcar width h = gDRV_ipipeObj.boxcarInfo.height; // Boxcar height
2. Затем по этим данным строится гистограмма и вычисляется среднее значение Y.
Uint32 sz = w*h, sz4 = sz*4, hsz = 512; Uint32 hist[hsz]; int GN[3] = { -16, 0, 16};
memset (hist, 0, sizeof (Uint32)*hsz); //AE and WB for (i=0; i < sz4; i+=4) { r = box[i+2]>>2; g = box[i+1]>>2; b = box[i ]>>2;
RR += r; GG += g; BB += b; Y += ((117*b + 601*g + 306*r)>>10);
hist[r>>3]++; hist[g>>3]++; hist[b>>3]++;
for (j=0; j < ns; j++) { GB[j] += abs(g - (b*(512 + GN[j])>>9)); GR[j] += abs (g — (r*(512 + GN[j])>>9)); } }
Y = Y/sz; hn→Y.New = Y; RR = RR/sz; GG = GG/sz; BB = BB/sz;
3. Находим минимальное значение гистограммы. //Find histogram min sum = 0; for (i=0; sum < hn->SatTh; i++) sum += hist[i]; hn→Hmin.New = i; //Find histogram max sum = 0; for (i=hsz-1; sum < hn->SatTh; i--) sum += hist[i]; hn→Hmax.New = i;
4. Меняем значение экспозиции. Если величина среднего значения Y в два раза больше или меньше нашего порога YAE, то шаг изменения увеличивается, a если изменения Y меньше 20%, то экспозиция не меняется. if (hn→Y.New) tmp = (hn→Y.New > hn→YAE) ? hn→Y.New*100/hn→YAE: hn→YAE*100/hn→Y.New; if (tmp > 200){ if (hn→Y.New) hn→Exp.New = hn→Exp.Old*(hn→Y.New*2 + hn→YAE)/(hn→Y.New*3); } else if (tmp > 20){ if (hn→Y.New > hn→YAE) hn→Exp.New = hn→Exp.Old*99/100; else hn→Exp.New = hn→Exp.Old*100/99; }
if (hn→Exp.New > hn→Exp.Range.max) hn→Exp.New = hn→Exp.Range.max;
5. Для плавного изменения картинки видеопотка делаем усреднение нескольких последних значений величин, используемых в AE алгоритме: #define HISTORY 30 int history = 0;
typedef struct IAEWBF_Param{ XDAS_Int32 Old; //Old value XDAS_Int32 New; //New value XDAS_Int32 Step; //The step of changing XDAS_Int32 Avrg; //Sum of all history value XDAS_Int32 Change; //Need for smooth change XDAS_Int32 Hist[HISTORY]; //History array XDAS_Int32 HistC; //History count XDAS_Int32 NewA; //Avarage of value IAEWBF_Range Range; //The range of value changes }IAEWBF_Param;
int add_history (IAEWBF_Param *p) { int diff = 0; p→Avrg += p→New; p→Avrg -= p→Hist[p→HistC]; if (p→New) diff = abs (p→Hist[p→HistC] — p→New)*100/p→New; p→Hist[p→HistC] = p→New; p→HistC = (p→HistC == (HISTORY — 1)) ? 0: p→HistC + 1; p→NewA = (history < HISTORY) ? p->Avrg/history: p→Avrg/HISTORY; return diff; } history++; add_history (&hn→Hmax); add_history (&hn→Hmin); add_history (&hn→Y);
6. И, наконец, последний этап AE — установка усиления и сдвига. На выходе ориентируемся на то, чтобы среднее значение Y попало примерно на середину выходного диапазона HmaxTh. С учетом гамма коррекции и эксперимента это значение равно hn→HmaxTh/4. //Change the offset and gain hn→Offset.New = hn→Hmin.NewA; if (hn→Y.NewA — hn→Offset.New) hn→GIFIF.New = ((hn→HmaxTh/4)*512)/(hn→Y.NewA — hn→Offset.New); up = hn→Hmax.NewA*hn→GIFIF.New>>9; if ((up < hn->HmaxTh) && (hn→Y.NewA — hn→Offset.New)) if (hn→Y.NewA — hn→Offset.New) hn→GIFIF.New = (((hn→HmaxTh*2 — up)/4)*512)/(hn→Y.NewA — hn→Offset.New);
//Check gain range hn→GIFIF.New = hn→GIFIF.New > hn→GIFIF.Range.max? hn→GIFIF.Range.max: hn→GIFIF.New; hn→GIFIF.New = hn→GIFIF.New < hn->GIFIF.Range.min? hn→GIFIF.Range.min: hn→GIFIF.New;
Теперь перейдем к балансу белого (WB).Существует множество алгоритмов его настройки, мы в своей камере используем 2 из них.В дневном режиме минимизируем локальные отклонения цветов по всей картинке. Так как у белого, черного и серого R = G = B, а белый еще и самый яркий в сцене, то его минимизация вносит наибольший вклад в суммарную энергию.Ночью же при открытом инфракрасном фильтре применяем grey world алгоритм, то есть просто выравниваем цветовые средние.В основном цикле при заполнение гистограмм мы ищем значения суммарных отклонений красного GR[j] и синего GB[j] от зеленого и находим направление движения к минимуму: if (IRcutClose){ min = GR[0]; minr = 0; for (j=1; j < ns; j++){ if(GR[j] < min) { min = GR[j]; minr = j; } } min = GB[0]; minb = 0; for(j=1; j < ns; j++){ if(GB[j] < min) { min = GB[j]; minb = j; } } if(minr != 1) hn->Rgain.New = hn→Rgain.Old + (GN[minr]*hn→Rgain.Old/512); if (minb!= 1) hn→Bgain.New = hn→Bgain.Old + (GN[minb]*hn→Bgain.Old/512);
} else { //Night AW mode if (RR) hn→Rgain.New = GG*hn→Rgain.Old/RR; if (BB) hn→Bgain.New = GG*hn→Bgain.Old/BB; }
Разные сенсоры имеют совершено разные цветовые отклонения, но алгоритм днем работает всегда корректно, пример для сенсора SONY IMX136 до и после баланса белого:
Немного остановимся на гамма коррекции, она необходима для того, что бы выровнять чувствительность камеры и человеческого глаза. Как правило функция преобразования имеет степенную зависимость, но мы применяем другую, по нашим субъективным ощущениям она лучше отображает действительность:
gam[i] = out*((log (i + in*a) — log (in*a))/(log (in + in*a) — log (in*a)));
где in — максимум входного диапазона, out — максимум выходного диапазона, a — коэффициент кривизны (в нашем случае 0.05 лучше всего подходит для разных сцен)
При инициализации системы мы загружаем эту кривую в LUT таблицу и каждый кадр проходит через нее. Пример как меняет картинку гамма коррекция:
Хотелось бы еще упомянуть о возможностях наших камер, которые не очень часто встречаются. Во-первых, переключение на пониженную частоту кадров при снижении освещенности, во-вторых, в ночном режиме, когда убирается инфракрасный фильтр, камера может оставаться в цвете.
Исходный код прошивки наших камер доступен через систему управления версиями Git по адресу: git://sigrand.ru/sigticam.git. Чтобы загрузить исходный код вы можете использовать следующую команду: git clone git://sigrand.ru/sigticam.git.Выше описанные алгоритмы находятся в директории sigticam/sigticam/platform/ti_dm368/appro2/av_capture/framework/alg/src/aewbf_sigПрямую трансляцию с наших, и не только наших, камер можно посмотреть здесь.В следующей статье мы рассмотрим как реализован в наших камерах автофокуси что такое широкий динамичесикй диапазон HDR или WDR.
.