Bilinear interpolation is the default Image Scaling Algorithm Used in opencv. Its effect and speed are both good. The results are also relatively stable, and the computing complexity is not too high. I have read a lot of online algorithms, and I have not understood them too well. The following is an explanation of bilinear interpolation algorithms found on the Internet.
"In the bilinear interpolation amplification algorithm of the image, the new pixel value in the target image is, the value of four neighboring pixels in the 2*2 area near the source image is calculated by weighted average. After the bilinear interpolation algorithm is enlarged, the image quality is high and the pixel value does not appear discontinuous. However, the secondary algorithm has a low-pass filter, which damages the high-frequency components. Therefore, the image contour may be blurred to a certain extent ."
Let's continue with the story based on my own understanding. I believe there are many masters in the book. I hope the readers can give me some advice to better understand it.
The bilinear interpolation algorithm is similar to the nearest neighbor interpolation algorithm. In the nearest neighbor interpolation algorithm, a point (x, y) in the target image is used to find the nearest vertex (x0, y0) in the source image. The points (x, y) in the target image correspond to the points (x0', y0') in the source image. x0' and y0' are probably not integers but decimal places, the nearest neighbor interpolation algorithm is to find its adjacent integer values (INT (x0 '+ 0.5f), INT (y0' + 0.5f) (not rounded in the previous article ). We are now looking for the four points next to the x0' and y0' positions. Based on the relationship between these four points and the distance (x0', y0'), we can calculate the (x, y) of the target image) A pixel value. The algorithm is described as follows:
(1) calculate the ratio of source image to target image width to height
W0: indicates the source image width.
H0: indicates the height of the source image.
W1: indicates the width of the target image.
H1: height of the Target Image
Float fw = float (w0-1)/(w1-1 );
Float FH = float (h0-1)/(h1-1 );
(2) For a point (x, y) of the target image, the corresponding coordinates in the source image are calculated and the result is a floating point number.
Float X0 = x * Fw;
Float Y0 = y * FH;
Int X1 = int (x0 );
Int X2 = X1 + 1;
Int Y1 = int (y0 );
Int y2 = Y1 + 1;
The four coordinate points in the source image are (x1, Y1) (x1, Y2) (X2, Y1) (X2, Y2)
(3) Calculate the weight ratio of the four surrounding points
For example,
Fx1 = x0-x1;
Fx2 = 1.0f-fx1;
Fy1 = y0-Y1;
FY2 = 1.0f-fy1;
Float S1 = fx1 * fy1;
Float S2 = fx2 * fy1;
Float S3 = fx2 * FY2;
Float S4 = fx1 * FY2;
We use value (coordinate) to obtain the coordinate value of this point, then:
Value (x0, y0) = value (X2, Y2) * S1 + value (x1, Y2) * S2 + value (x1, Y1) * S3 + value (X2, Y1) * S4;
If you are not clear about the above operations, you can do so.
We must first obtain the pixel values (x0, Y1) and (x0, Y2.
Float value (x0, Y1) = value (x1, Y1) * fx2 + value (X2, Y1) * fx1;
Float value (x0, Y2) = value (x1, Y2) * fx2 + value (X2, Y2) * fx1;
Note: The closer it is to a certain point, the greater the distance from the weight, so the difference between it and 1 is obtained.
Float value (x0, y0) = value (x0, Y1) * FY2 + value (x0, Y2) * fy1;
After verification, it is the same as the above formula.
(4) Fill in the target image after obtaining the value.
To make it easier to understand, use getpixel and setpixel for values and values.
void ResizeLinear01(CImage& src, CImage& dst)
{
int w0 = src.GetWidth();
int h0 = src.GetHeight();
int pitch0 = src.GetPitch();
int w1 = dst.GetWidth();
int h1 = dst.GetHeight();
int pitch1 = dst.GetPitch();
float fw = float(w0) / w1;
float fh = float(h0) / h1;
int y1,y2, x1,x2, x0,y0;
float fx1,fx2, fy1, fy2;
for(int y=0; y {
y0 = y*fh;
y1 = int(y0);
if(y1 == h0-1) y2 = y1;
else y2 = y1 + 1;
fy1 = y1-y0;
fy2 = 1.0f - fy1;
for(int x=0; x<w1; x++)
{
x0 = x*fw;
x1 = int(x0);
if(x1 == w0-1) x2 = x1;
else x2 = x1+1;
fx1 = y1-y0;
fx2 = 1.0f - fx1;
float s1 = fx1*fy1;
float s2 = fx2*fy1;
float s3 = fx2*fy2;
float s4 = fx1*fy2;
COLORREF c1,c2,c3,c4, color;
c1 = src.GetPixel(x1,y1);
c2 = src.GetPixel(x2,y1);
c3 = src.GetPixel(x1,y2);
c4 = src.GetPixel(x2,y2);
BYTE r,g,b;
r = (BYTE)(GetRValue(c1)*s3) + (BYTE)(GetRValue(c2)*s4) + (BYTE)(GetRValue(c3)*s2) + (BYTE)(GetRValue(c4)*s1);
g = (BYTE)(GetGValue(c1)*s3) + (BYTE)(GetGValue(c2)*s4) + (BYTE)(GetGValue(c3)*s2) + (BYTE)(GetGValue(c4)*s1);
b = (BYTE)(GetBValue(c1)*s3) + (BYTE)(GetBValue(c2)*s4) + (BYTE)(GetBValue(c3)*s2) + (BYTE)(GetBValue(c4)*s1);
dst.SetPixelRGB(x, y, r, g, b);
}
}
}
The test program still scales the image in the size of 670*503 to 200*160. After testing, it takes about 0.5 seconds to execute the above algorithm. It can be said that it is very slow. It takes 50 seconds to scale an image to 2000*1600 at a time. This is terrible. If we create a software similar to Photoshop, the user wants to multiply it several times, but it will take 50 seconds. It is estimated that the user has no patience to use your software.
Let's analyze how the program can be optimized. After each optimization algorithm step, we use the same program and steps above to test and observe the running time.
(1) Change the function call to pointer operations, as shown in section 1st. (After this step, the program takes about 0.2 seconds, and the speed is increased by 250 times. Haha !)
(2) extract the calculation of the x0 and Y0 coordinates out of the loop, because the X coordinates in the second loop must be repeated once each time and are repeated. (It will still take about 0.2 seconds. Let's loop it for one hundred times and compare the time used. It takes 19.6 seconds to use the resizelinear02 method and 17.4 seconds to use the resizelinear02 method .)
The following is the final code.
void ResizeLinear04(CImage& src, CImage& dst)
{
int w0 = src.GetWidth();
int h0 = src.GetHeight();
int pitch0 = src.GetPitch();
int w1 = dst.GetWidth();
int h1 = dst.GetHeight();
int pitch1 = dst.GetPitch();
BYTE* pSrc = (BYTE*)src.GetBits();
BYTE* pDst = (BYTE*)dst.GetBits();
BYTE* p0, *p1 = pDst;
float fw = float(w0-1) / (w1-1);
float fh = float(h0-1) / (h1-1);
float x0, y0;
int y1, y2, x1, x2;
float fx1, fx2, fy1, fy2;
int* arr_x1 = new int[w1];
int* arr_x2 = new int[w1];
float* arr_fx1 = new float[w1];
for(int x=0; x<w1; x++)
{
x0 = x*fw;
arr_x1[x] = int(x0);
arr_x2[x] = int(x0+0.5f);
arr_fx1[x] = x0 - arr_x1[x];
//TRACE(L"x=%6d; x0=%6.3f; x1=%6d; x2=%6d; fx1=%6.3f;\n", x, x0, arr_x1[x], arr_x2[x], arr_fx1[x]);
}
for(int y=0; y {
y0 = y*fh;
y1 = int(y0);
y2 = int(y0+0.5f);
fy1 = y0-y1;
fy2 = 1.0f - fy1;
//TRACE(L"y=%6d; y0=%6.3f; y1=%6d; y2=%6d; fy1=%6.3f;\n", y, y0, y1, y2, fy1);
for(int x=0; x<w1; x++)
{
x1 = arr_x1[x];
x2 = arr_x2[x];
fx1 = arr_fx1[x];
fx2 = 1.0f-fx1;
float s1 = fx2*fy2;
float s2 = fx1*fy2;
float s3 = fx1*fy1;
float s4 = fx2*fy1;
//TRACE(L"s1=%6.3f; s2=%6.3f; s3=%6.3f; s4=%6.3f; sum=%6.3f\n", s1,s2,s3,s4, s1+s2+s3+s4);
BYTE* p11 = pSrc + pitch0*y1 + 3*x1;
BYTE* p12 = pSrc + pitch0*y1 + 3*x2;
BYTE* p21 = pSrc + pitch0*y2 + 3*x1;
BYTE* p22 = pSrc + pitch0*y2 + 3*x2;
*p1 = BYTE((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++; p11++; p12++; p21++; p22++;
*p1 = BYTE((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++; p11++; p12++; p21++; p22++;
*p1 = BYTE((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++;
}
p1 = pDst + y*pitch1;
}
delete []arr_x1;
delete []arr_x2;
delete []arr_fx1;
}