Алгоритм стабилизации изображения/выравнивания [закрыто

StackOverflow https://stackoverflow.com/questions/3532133

Вопрос

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

Для моей цели не будет панорамирования или масштабирования видео и движущихся объектов в кадрах.

Это было полезно?

Решение

Сначала вы можете попробовать несколько простых подходов, я предложил несколько недавно здесь: Стабилизация видео с OpenCV

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

Для моих потребностей я внедрил простой инструмент, который идет по маршруту серфинга/гомографии, чтобы выровнять несколько изображений (выравнивает изображения, а не видео). Вы можете попробовать это, чтобы увидеть, достаточно ли этого и ваших потребностей: http://web.archive.org/web/20101004234404/http://ioctl.eu/wiki/applications/imagealign (Хех, я надеюсь, что код все еще работает ...)

Другие советы

Я написал простую функцию, которая стабилизирует три буфера видео кадра, один для красного, зеленого и синего. Это не самый быстрый, но для DVD-каркаса NTSC (720x480) он может работать со скоростью около 1,7 кадров в секунду на электронных Machines T6412. Все, что вам нужно сделать, это выбрать место в центре изображения и сравнить его с последним кадром, накапливая, сколько бит в каждом матче пикселей. В вложенной петле смещено область поиска самого последнего кадра на один пиксель для каждой итерации цикла. Я попробовал аналоговую корреляцию FIR, но это не сработало почти так же хорошо, как выбрать смещение выравнивания с самыми подходящими битами. И этот алгоритм также делает выходное видео заполнять экран вместо того, чтобы разматывать черные полосы, которые следуют за камерой камеры.

#define min(a, b)  (((a) < (b)) ? (a) : (b)) 
#define max(a, b)  (((a) > (b)) ? (a) : (b))

int cubic(int x,int *v)
{  
    int q1,q2,q3,q4;
    q1=( -1792 * v[0] + 5376 * v[1] - 5376 * v[2] + 1792 * v[3] ) >> 8;
    q2=( 3840 * v[0] - 9216 * v[1] + 6912 * v[2] - 1536 * v[3] ) >> 8;
    q3=( -2304 * v[0] + 2304 * v[2] ) >> 8;
    q4=(v[0] + 4096 * v[1] + v[2]) >> 8;
    return  (((((((((q1 * x) >> 8) + q2 ) * x ) >> 8)+ q3 ) * x) >> 8) + q4 ) >> 4;
}


double get_mono_cubic_row(unsigned char * redbuf,unsigned long width, unsigned long height,signed long x,signed long y,signed int offset)
{
    int m[4]={0,0,0,0};
    if(x+3<width && x>=0 && y<height && y>0)
    {

        m[0]=redbuf[x+y*width];
        m[1]=redbuf[(x+1)+y*width];
        m[2]=redbuf[(x+2)+y*width];
        m[3]=redbuf[(x+3)+y*width];

    }
    else
    {
        m[0]=255;
        m[1]=255;
        m[2]=255;
        m[3]=255;
    }

    return cubic(offset,m);


}

unsigned char get_mono_bicubic (unsigned char *redbuf, unsigned long width,unsigned long height,double x, double y)
{
    int xi=0,yi=0;
    int dx=0,dy =0;
    int  m[4]={0.0,0.0,0.0,0.0}; /* four of one mono pixel */
    xi=floor(x);
    yi=floor(y);
    dx=(x-xi) * 256;
    dy=(y-yi) * 256;
    if(yi+3<height && xi>0 && yi>0 && xi<width)
    {
        m[0]=get_mono_cubic_row(redbuf,width,height,xi-1,yi-1,dx);
        m[1]=get_mono_cubic_row(redbuf,width,height,xi-1,yi,dx);
        m[2]=get_mono_cubic_row(redbuf,width,height,xi-1,yi+1,dx);
        m[3]=get_mono_cubic_row(redbuf,width,height,xi-1,yi+2,dx);
    }
    else
    {
        m[0]=255;
        m[1]=255;
        m[2]=255;
        m[3]=255;
    }


    return clip(cubic(dy,m));

}


void mono_scale_exact(unsigned char *redbuf,unsigned long width, unsigned long height,unsigned long desired_width, unsigned long desired_height)
{
    unsigned char *tempbuf=NULL;
    double ratio_x=(desired_width * (1.0/(double)width));
    double ratio_y=(desired_height * (1.0/(double)height));
    unsigned long maxwidth=1;
    unsigned long maxheight=1;
    double u=0;
    int x=0;
    int y=0;
    double v=0;
    maxwidth=max(desired_width,width);
    maxheight=max(desired_height,height);
    tempbuf=(unsigned char*)malloc(maxwidth * maxheight * sizeof(unsigned char));
    if(tempbuf!=NULL)
    {
        /* first the red */
        for(y=0;y<desired_height;y++)
        {
            for(x=0;x<desired_width;x++)
            {
                u = x * (1.0/ratio_x);
                v = y * (1.0/ratio_y);
                tempbuf[x+y*desired_width]=get_mono_bicubic (redbuf,width,height,u,v);
            }
        }  
        for(y=0;y<desired_height;y++)
        {
            for(x=0;x<desired_width;x++)
            {
                redbuf[x+y*desired_width]=tempbuf[x+y*desired_width];
            }
        }
        free(tempbuf);  
    }

  } 



void fatal(void)
{
    exit(1);
}

#define DEBUG_STABLE 0
unsigned char digital_image_stabilization(unsigned char *redbuf, unsigned char *greenbuf, unsigned char *bluebuf,
unsigned long width, unsigned long height, unsigned short search_len,unsigned short twiddle)
{
    static unsigned char *search_scratch=NULL;
    static unsigned char *tempbuf=NULL;
    unsigned long in_x=0;
    unsigned long in_y=0;
    static signed long x_adj=0;
    static signed long y_adj=0;
    unsigned long out_x=0;
    const unsigned int mask[8]={1,2,4,8,16,32,64,128};
    unsigned long out_y=0;
    signed long mid_x=0;
    signed long mid_y=0;
    static signed long center_x=0;
    static signed long center_y=0;
    static signed long end_center_x=0;
    static signed long end_center_y=0;
    static unsigned char first=1;
    int search_x=0;
    int search_y=0;
    unsigned long peak=0;
    static int new_width=0;
    static int new_height=0;
    int tww_y=0;
    int twp_y=0;
    static unsigned long *twiddle_scratch=NULL;
    if(first==1)
    {
        center_x=(width/2)-(search_len/2);
        if(center_x<twiddle)center_x=twiddle;
        center_y=(height/2)-(search_len/2);
        if(center_y<twiddle)center_y=twiddle;
        if((search_len+center_x)>width)fatal();
        if((search_len+center_y)>height)fatal();
        end_center_y=center_y+search_len;
        end_center_x=center_x+search_len;
        new_width=width-twiddle;
        new_height=height-twiddle;
        search_scratch=(unsigned char *)malloc((search_len * search_len) * sizeof(unsigned char));
        tempbuf=(unsigned char *)malloc((width * height) * sizeof(unsigned char));
        twiddle_scratch=(unsigned long *)malloc((twiddle * twiddle) * sizeof(unsigned long));
        if(search_scratch==NULL || tempbuf==NULL || twiddle_scratch==NULL)fatal();
        first=0;
    }
    for(search_y=0;search_y<twiddle;search_y++)
    {
        for(search_x=0;search_x<twiddle;search_x++)
        {
            twiddle_scratch[search_x+search_y]=0;
        }
    }

    /* Multiply-accumulate */
    for(mid_y=0;mid_y<twiddle;mid_y++)
    {
        int twp_x=0;
        for(mid_x=0;mid_x<twiddle;mid_x++)
        {
            unsigned long acc=0;
            int tw_y=0;
            for(in_y=center_y;in_y<end_center_y;in_y++)
            {
                int tw_x=0;
                for(in_x=center_x;in_x<end_center_x;in_x++)
                {
                    unsigned long bmpptr=(in_x+mid_x)+(in_y+mid_y)*width;
                    unsigned int cur_gray=((((77 * redbuf[bmpptr])+(151 * greenbuf[bmpptr]) + (28 * bluebuf[bmpptr])) >> 8) & 255);
                    unsigned int last_gray=search_scratch[tw_x+tw_y*search_len];
                    acc+=(!((last_gray ^ cur_gray) & mask[0]));
                    acc+=(!(((last_gray ^ cur_gray) & mask[1]) >> 1));
                    acc+=(!(((last_gray ^ cur_gray) & mask[2]) >> 2));
                    acc+=(!(((last_gray ^ cur_gray) & mask[3]) >> 3));
                    acc+=(!(((last_gray ^ cur_gray) & mask[4]) >> 4));
                    acc+=(!(((last_gray ^ cur_gray) & mask[5]) >> 5));
                    acc+=(!(((last_gray ^ cur_gray) & mask[6]) >> 6));
                    acc+=(!(((last_gray ^ cur_gray) & mask[7]) >> 7));
                    tw_x++;
                }
                tw_y++;
            }
            //acc/=(search_len * search_len);
            twiddle_scratch[twp_x+twp_y*twiddle]=acc;
            twp_x++;
        }
        twp_y++;
    }

    for(search_y=0;search_y<twiddle;search_y++)
    {
        for(search_x=0;search_x<twiddle;search_x++)
        {
            if(twiddle_scratch[search_x+search_y*twiddle]>peak)
            {
                peak=twiddle_scratch[search_x+search_y*twiddle];
                x_adj=search_x;
                y_adj=search_y;
            }
        }
    }
    /* update the scratch area with the stabilized image */
    tww_y=0;
    for(in_y=center_y;in_y<end_center_y;in_y++)
    {
        int tww_x=0;
        for(in_x=center_x;in_x<end_center_x;in_x++)
        {
            unsigned long out_bmpptr=tww_x+tww_y*search_len;
            #if !DEBUG_STABLE
            signed long safe_x=(in_x+x_adj);
            signed long safe_y=(in_y+y_adj);
            #endif
            #if DEBUG_STABLE
            signed long safe_x=(in_x-x_adj);
            signed long safe_y=(in_y-y_adj);
            #endif
            unsigned long in_bmpptr=0;

            unsigned char cur_gray=0;
            if(safe_x<0)safe_x=0;
            if(safe_y<0)safe_y=0;
            in_bmpptr=safe_x+safe_y*width;
            cur_gray=((77 * redbuf[in_bmpptr])+(151 * greenbuf[in_bmpptr]) + (28 * bluebuf[in_bmpptr])) >> 8;
            search_scratch[out_bmpptr]=cur_gray;
            tww_x++;
        }
        tww_y++;
    }
    /* move red */
    for(out_y=twiddle;out_y<height;out_y++)
    {
        for(out_x=twiddle;out_x<width;out_x++)
        {
            signed long out_bmpptr=(out_x-twiddle)+(out_y-twiddle)*new_width;
            #if !DEBUG_STABLE
            signed long safe_x=((out_x-twiddle)+x_adj);
            signed long safe_y=((out_y-twiddle)+y_adj);
            #endif

            #if DEBUG_STABLE
            signed long safe_x=(out_x-x_adj);
            signed long safe_y=(out_y-y_adj);

            unsigned long bad_bmpptr=out_x+out_y*width;
            #endif
            signed long in_bmpptr=0;
            if(safe_x<0)safe_x=0;
            if(safe_y<0)safe_y=0;
            if(safe_x>width)safe_x=width;
            if(safe_y>height)safe_y=height;
            in_bmpptr=safe_x+safe_y*width;
            #if DEBUG_STABLE
            tempbuf[out_bmpptr]=((safe_x>center_x-8 && safe_x<center_x+8) && (safe_y>center_y-8 && safe_y<center_y+8)) ? 255 :redbuf[bad_bmpptr];
            #endif
            #if !DEBUG_STABLE
            tempbuf[out_bmpptr]=redbuf[in_bmpptr];
            #endif
        }
    }
    mono_scale_exact(tempbuf,new_width,new_height,width,height);
    for(out_y=0;out_y<height;out_y++)
    {
        for(out_x=0;out_x<width;out_x++)
        {
            unsigned long bmpptr=out_x+out_y*width;
            redbuf[bmpptr]=tempbuf[bmpptr];
        }
    }
    /* move green */
    for(out_y=twiddle;out_y<height;out_y++)
    {
        for(out_x=twiddle;out_x<width;out_x++)
        {
            signed long out_bmpptr=(out_x-twiddle)+(out_y-twiddle)*new_width;
            #if !DEBUG_STABLE
            signed long safe_x=((out_x-twiddle)+x_adj);
            signed long safe_y=((out_y-twiddle)+y_adj);
            #endif

            #if DEBUG_STABLE
            signed long safe_x=(out_x-x_adj);
            signed long safe_y=(out_y-y_adj);

            unsigned long bad_bmpptr=out_x+out_y*width;
            #endif
            signed long in_bmpptr=0;
            if(safe_x<0)safe_x=0;
            if(safe_y<0)safe_y=0;
            if(safe_x>width)safe_x=width;
            if(safe_y>height)safe_y=height;         
            in_bmpptr=safe_x+safe_y*width;
            #if DEBUG_STABLE
            tempbuf[out_bmpptr]=((safe_x>center_x-8 && safe_x<center_x+8) && (safe_y>center_y-8 && safe_y<center_y+8)) ? 0 :greenbuf[bad_bmpptr];
            #endif
            #if !DEBUG_STABLE
            tempbuf[out_bmpptr]=greenbuf[in_bmpptr];
            #endif

        }
    }
    mono_scale_exact(tempbuf,new_width,new_height,width,height);
    for(out_y=0;out_y<height;out_y++)
    {
        for(out_x=0;out_x<width;out_x++)
        {
            unsigned long bmpptr=out_x+out_y*width;
            greenbuf[bmpptr]=tempbuf[bmpptr];
        }
    }
    /* move blue */
    for(out_y=twiddle;out_y<height;out_y++)
    {
        for(out_x=twiddle;out_x<width;out_x++)
        {
            signed long out_bmpptr=(out_x-twiddle)+(out_y-twiddle)*new_width;
            #if !DEBUG_STABLE
            signed long safe_x=((out_x-twiddle)+x_adj);
            signed long safe_y=((out_y-twiddle)+y_adj);
            #endif
            #if DEBUG_STABLE
            signed long safe_x=(out_x-x_adj);
            signed long safe_y=(out_y-y_adj);

            unsigned long bad_bmpptr=out_x+out_y*width;
            #endif
            signed long in_bmpptr=0;
            if(safe_x<0)safe_x=0;
            if(safe_y<0)safe_y=0;
            if(safe_x>width)safe_x=width;
            if(safe_y>height)safe_y=height;
            in_bmpptr=safe_x+safe_y*width;
            #if DEBUG_STABLE
            tempbuf[out_bmpptr]=((safe_x>center_x-8 && safe_x<center_x+8) && (safe_y>center_y-8 && safe_y<center_y+8)) ? 255 :bluebuf[bad_bmpptr];
            #endif
            #if !DEBUG_STABLE
            tempbuf[out_bmpptr]=bluebuf[in_bmpptr];
            #endif
        }
    }
    mono_scale_exact(tempbuf,new_width,new_height,width,height);
    for(out_y=0;out_y<height;out_y++)
    {
        for(out_x=0;out_x<width;out_x++)
        {
            unsigned long bmpptr=out_x+out_y*width;
            bluebuf[bmpptr]=tempbuf[bmpptr];
        }
    }
    return (x_adj==0 && y_adj==0) ? 0 : 1;
}

Чтобы использовать его, заполните *redbuf с красным каналом изображения, *greenbuf с зеленым каналом, *bluebuf с синим каналом и вызовите функцию Digital_image_stabilization. Как только функция завершится, буферы будут иметь стабилизированное изображение. Я использовал 96 для search_len и 32 для Twiddle.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top