從我已閱讀在網絡上,對於image stabilization最有效的算法之一是使用格雷編碼位平面匹配。然而,我很難理解它(格雷碼本身並不那麼複雜,只是其餘部分)。任何人都可以向我指出一個關於這個問題的資源(或者另一個很好的穩定方法),它比大多數已發表論文的水平略低一點?示例代碼優於抽象的廣義方程。圖像穩定/校準算法
對於我的目的,不會有平移或視頻的縮放和幀沒有移動物體。
從我已閱讀在網絡上,對於image stabilization最有效的算法之一是使用格雷編碼位平面匹配。然而,我很難理解它(格雷碼本身並不那麼複雜,只是其餘部分)。任何人都可以向我指出一個關於這個問題的資源(或者另一個很好的穩定方法),它比大多數已發表論文的水平略低一點?示例代碼優於抽象的廣義方程。圖像穩定/校準算法
對於我的目的,不會有平移或視頻的縮放和幀沒有移動物體。
你可以先嚐試一些簡單的方法,我已經提出了一些最近的位置:Video Stabilization with OpenCV
在你的情況(無鍋,不進行變焦,靜態場景),相位相關可能已經足夠,它很容易(例如參見wikipedia)。 如果我沒有記錯,有幾個視頻穩定濾波器/使用相位相關供AviSynth的插件 - 你可以先嚐試出來。
對於我的需求,我實現了一個簡單的工具就是那張在SURF /單應路線對準多個圖像(圖像對齊,而不是視頻)。你可能想嘗試一下,看看它是否滿足你的需求:http://web.archive.org/web/20101004234404/http://ioctl.eu/wiki/applications/ImageAlign(嘿,我希望代碼仍然運行...)
既然你顯然不能在該領域的專家,我會建議避免追算法目前「最先進的」,因爲你不會是能夠在最近的文章中可靠地評估各種索賠。
下面是一些代碼和文件(「多運動的魯棒估計:參數和分段暢通場」)的鏈接以更少的先決條件: http://www.cs.brown.edu/~black/code.html
如果你能看懂這紙你應該能夠建立一個體面的圖像穩定/對齊系統與相關的代碼。
我寫了一個簡單的函數,穩定一個視頻幀的三個緩衝區,一個紅色,綠色和藍色。這不是最快的,但對於NTSC DVD幀(720x480),它可以在E-machines t6412上以每秒1.7幀的速度運行。您只需在圖像中心選取一個點並通過累加每個像素中匹配的位數來將其與最後一幀進行比較。在嵌套循環中,爲每次循環迭代將最近幀的搜索區域偏移一個像素。我嘗試了模擬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功能。一旦函數完成,緩衝區將具有穩定的圖像。我用search_len用96,用twiddle用32。
ImageAlign鏈接404' - 任何更新的URL的機會? :) – taber 2012-02-01 02:01:49
@taber:Wayback'd – genpfault 2015-04-24 02:40:07
@genpfault謝謝!雖然我指的是.zip下載鏈接,但在Wayback Machine鏈接上仍然存在問題。 – taber 2015-04-24 06:22:41