快速快速双线性插值

图像缩放是最常用的图像操作,最近实现了一次双线性插值,适用于单通道8位灰度图像,可以同时处理缩小,放大。

加速手段有三个:

1:使用可分离方式,先后处理行和列

2:构建缓冲区,以避免对某些行重复插值。

3:使用整形运算,避免浮点运算。

performace:

测试图像: C:\Documents and Settings\All Users\Documents\My Pictures\示例图片\Water lilies.jpg

scale

_ieInterpImageBilinear8UC1(ms)

cvResize(ms)

ratio

0.3

346

390

89%

0.5

939

1047

90%

0.8

1671

1507

110%

1.2

2885

1943

148%

1.5

3951

2678

150%

2

6119

3642

170%

上表都是执行1000次的总时间,可以看出,在缩小时速度比opencv的cvResize相当,在放大时,不幸的比cvResize慢上50%。贴出代码求解。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
typedef struct _Image{
     int w;
     int h;
     int c;
     int step;
     int type;
     void * data;
} Image;
 
static void _ieInterpImageBilinear8UC1_RowFilter(unsigned char * src, long * dst, int len, int * leftIdx, int * rightIdx, long * weight, int shift)
{
     int i, j;
     for (i = 0, j = 1; i < len - 2; i+=2, j+=2){
         dst[i] = ((1<<shift) - weight[i])*src[leftIdx[i]] + weight[i]*src[rightIdx[i]];
         dst[j] = ((1<<shift) - weight[j])*src[leftIdx[j]] + weight[j]*src[rightIdx[j]];
 
     }
     for ( ; j < len; j++){
         dst[j] = ((1<<shift) - weight[j])*src[leftIdx[j]] + weight[j]*src[rightIdx[j]];
     }
}
 
static void _ieInterpImageBilinear8UC1(Image* src, Image* dst)
{
     int i, j;
     int sw, sh, sstep;
     int dw, dh, dstep;
     unsigned char *sdata, *ddata;
     float horScaleRatio, verScaleRatio;
     long *rowBuf1, *rowBuf2;
     long *upLinePtr, *downLinePtr, *tempPtr;
     long *horWeight;
     int *horLeftIdx, *horRightIdx;
     int preVerUpIdx, preVerDownIdx;
     int shift = 8;
 
     sw = src->w; sh = src->h; sstep = src->step; sdata = (unsigned char *)(src->data);
     dw = dst->w; dh = dst->h; dstep = dst->step; ddata = (unsigned char *)(dst->data);
 
     horScaleRatio = sw / ( float )(dw);
     verScaleRatio = sh / ( float )(dh);
 
     rowBuf1 = new long [dw];
     rowBuf2 = new long [dw];
     horWeight = new long [dw];
     horLeftIdx = new int [dw];
     horRightIdx = new int [dw];
 
     //col interpolation
     //计算目标图像像素横向的左右邻居序号,和权重。
     for (i = 0; i < dw; i++){
         float pos = (i + 0.5f) * horScaleRatio;
         horLeftIdx[i] = ( int )(IET_MAX(pos - 0.5f, 0));
         horRightIdx[i] = ( int )(IET_MIN(pos + 0.5f, sw-1));
         horWeight[i] = ( long ) ( fabs (pos - 0.5f - horLeftIdx[i]) * (1<<shift));
     }
 
     preVerUpIdx = -1;
     preVerDownIdx = -1;
     upLinePtr = rowBuf1;
     downLinePtr = rowBuf2;
     for (j = 0; j < dh; j++){       
         float pos = (j + 0.5f) * verScaleRatio;
         int verUpIdx = ( int )(IET_MAX(pos - 0.5f, 0));
         int verDownIdx = ( int )(IET_MIN(pos + 0.5f, sh-1));
         long verWeight = ( long ) ( fabs (pos - 0.5f - verUpIdx) * (1<<shift));
 
         if (verUpIdx == preVerUpIdx && verDownIdx == preVerDownIdx){
             ; //do nothing
         }
         else if (verUpIdx == preVerDownIdx){
             IET_SWAP(upLinePtr, downLinePtr, tempPtr);
             _ieInterpImageBilinear8UC1_RowFilter(sdata + sstep*verDownIdx, downLinePtr, dw, horLeftIdx, horRightIdx, horWeight, shift);
         } else {
             _ieInterpImageBilinear8UC1_RowFilter(sdata + sstep*verUpIdx,   upLinePtr, dw, horLeftIdx, horRightIdx, horWeight, shift);
             _ieInterpImageBilinear8UC1_RowFilter(sdata + sstep*verDownIdx, downLinePtr, dw, horLeftIdx, horRightIdx, horWeight, shift);
         }       
 
         for (i = 0; i < dw; i++){
             ddata[dstep*j + i] = (unsigned char ) ( (((1<<shift) - verWeight)*upLinePtr[i] + verWeight*downLinePtr[i]) >> (2*shift) );
         }
         preVerUpIdx = verUpIdx;
         preVerDownIdx = verDownIdx;
     }
     delete []rowBuf1;
     delete []rowBuf2;
     delete []horWeight;
     delete []horLeftIdx;
     delete []horRightIdx;
}

使用神奇的四次展开循环,速度提升15%+。
code如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
static void _ieInterpImageBilinear8UC1_Ver3_RowFilter(unsigned char * src, long * dst, int len, int * leftIdx, int * rightIdx, long * weight, int shift)
{
     int i;
     for (i = 0; i < len - 4; i+=4){
         *dst++ = ((1<<shift) - weight[i])*src[leftIdx[i]] + weight[i]*src[rightIdx[i]];
         *dst++ = ((1<<shift) - weight[i+1])*src[leftIdx[i+1]] + weight[i+1]*src[rightIdx[i+1]];
         *dst++ = ((1<<shift) - weight[i+2])*src[leftIdx[i+2]] + weight[i+2]*src[rightIdx[i+2]];
         *dst++ = ((1<<shift) - weight[i+3])*src[leftIdx[i+3]] + weight[i+3]*src[rightIdx[i+3]];
 
     }
     for ( ; i < len; ++i){
         *dst++ = ((1<<shift) - weight[i])*src[leftIdx[i]] + weight[i]*src[rightIdx[i]];
     }
}
 
static void _ieInterpImageBilinear8UC1_Ver3(Image* src, Image* dst)
{
     int i, j;
     int sw, sh, sstep;
     int dw, dh, dstep;
     unsigned char *sdata, *ddata;
     float horScaleRatio, verScaleRatio;
     long *rowBuf1, *rowBuf2;
     long *upLinePtr, *downLinePtr, *tempPtr;
     long *horWeight;
     int *horLeftIdx, *horRightIdx;
     int preVerUpIdx, preVerDownIdx;
     int shift = 8;
 
     sw = src->w; sh = src->h; sstep = src->step; sdata = (unsigned char *)(src->data);
     dw = dst->w; dh = dst->h; dstep = dst->step; ddata = (unsigned char *)(dst->data);
 
     horScaleRatio = sw / ( float )(dw);
     verScaleRatio = sh / ( float )(dh);
 
     rowBuf1 = new long [dw];
     rowBuf2 = new long [dw];
     horWeight = new long [dw];
     horLeftIdx = new int [dw];
     horRightIdx = new int [dw];
 
     //col interpolation
     //计算目标图像像素横向的左右邻居序号,和权重。
     for (i = 0; i < dw; i++){
         float pos = (i + 0.5f) * horScaleRatio;
         horLeftIdx[i] = ( int )(IET_MAX(pos - 0.5f, 0));
         horRightIdx[i] = ( int )(IET_MIN(pos + 0.5f, sw-1));
         horWeight[i] = ( long ) ( fabs (pos - 0.5f - horLeftIdx[i]) * (1<<shift));
     }
 
     preVerUpIdx = -1;
     preVerDownIdx = -1;
     upLinePtr = rowBuf1;
     downLinePtr = rowBuf2;
     for (j = 0; j < dh; j++){       
         float pos = (j + 0.5f) * verScaleRatio;
         int verUpIdx = ( int )(IET_MAX(pos - 0.5f, 0));
         int verDownIdx = ( int )(IET_MIN(pos + 0.5f, sh-1));
         long verWeight = ( long ) ( fabs (pos - 0.5f - verUpIdx) * (1<<shift));
 
         if (verUpIdx == preVerUpIdx && verDownIdx == preVerDownIdx){
             ; //do nothing
         }
         else if (verUpIdx == preVerDownIdx){
             IET_SWAP(upLinePtr, downLinePtr, tempPtr);
             _ieInterpImageBilinear8UC1_Ver3_RowFilter(sdata + sstep*verDownIdx, downLinePtr, dw, horLeftIdx, horRightIdx, horWeight, shift);
         } else {
             _ieInterpImageBilinear8UC1_Ver3_RowFilter(sdata + sstep*verUpIdx,   upLinePtr, dw, horLeftIdx, horRightIdx, horWeight, shift);
             _ieInterpImageBilinear8UC1_Ver3_RowFilter(sdata + sstep*verDownIdx, downLinePtr, dw, horLeftIdx, horRightIdx, horWeight, shift);
         }       
 
         unsigned char * _ptr = ddata + dstep*j;
         for (i = 0; i < dw-4; i+=4){
             *_ptr++ = (unsigned char ) ( (((1<<shift) - verWeight)*upLinePtr[i] + verWeight*downLinePtr[i]) >> (2*shift) );
             *_ptr++ = (unsigned char ) ( (((1<<shift) - verWeight)*upLinePtr[i+1] + verWeight*downLinePtr[i+1]) >> (2*shift) );
             *_ptr++ = (unsigned char ) ( (((1<<shift) - verWeight)*upLinePtr[i+2] + verWeight*downLinePtr[i+2]) >> (2*shift) );
             *_ptr++ = (unsigned char ) ( (((1<<shift) - verWeight)*upLinePtr[i+3] + verWeight*downLinePtr[i+3]) >> (2*shift) );
         }
         for (; i < dw; i++){
             *_ptr++ = (unsigned char ) ( (((1<<shift) - verWeight)*upLinePtr[i] + verWeight*downLinePtr[i]) >> (2*shift) );
         }
         preVerUpIdx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值