Алгоритмы обработки видео на процессоре 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.

    .
    Sigrand
    Company
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 10

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

      Угу, есть у меня камеры с такой «фичей». Брал как low lux. Они как раз это снижением FPS сделали. Похоже, неотключаемо. Я уже замучался с такими :(
      У вас, надеюсь, эта «фича» отключаема?
        +1
        Конечно все включается и отключается
        0
        Что такое Boxcar?
        update: понятно, это такое название блока конвейера вашего процессора
          0
          Это просто аппаратный усреднитель блоками 8x8 или 16x16, в предыдущей статье есть его обзор.
          0
          А как работает этот баланс белого, если смотрит на большую-большую лужайку?
            +1
            Надо попробовать найти такую лужайку и камеру поставить, думаю ничего страшного не будет, потому как там не монотонный цвет и есть светлые участки отражающие свет от травы, которые внесут максимальный вклад в баланс
            +1
            парни, вы круты!
              0
              А почему решили использовать Boxcar, а не Histogram?
                0
                У Histogram очень маленькое разрешение, не помню уже точно, 256 бит или еще меньше. А нам нужно было 512.
                И еще bpxcar нужен был для автофокуса.
                0
                вот интересно: с IMX 222 и hi3516 из коробки баланс белого довольно приличный.

                Там какой-то автоматический механизм?

                Only users with full accounts can post comments. Log in, please.