Original from: Blog Park small Kinko
Image scaling algorithm and speed optimization--(i) Nearest neighbor interpolation
Image scaling algorithm and speed optimization--(ii) bilinear interpolation
———————————————————— below are the original ——————————————————
Introduction to the No. 0 section
Image scaling algorithm is a frequently encountered problem in digital image processing algorithm. We often convert images of some size to other size images, such as zooming in or out of images. The resize () function in OpenCV is very convenient and highly efficient. The following is a prototype of the Cvresize function provided by OPENCV.
/****************************************************************************************************/
Image size Transformation
void cvresize (const cvarr* SRC, cvarr* dst, int interpolation=cv_inter_linear);
Src
Enter an image.
Dst
The output image.
Interpolation
Interpolation methods:
Cv_inter_nn-Nearest neighbor interpolation,
Cv_inter_linear-bilinear interpolation (default use)
Cv_inter_area-resampling using pixel relationships. This method avoids ripples when the image shrinks. When the image is zoomed in, it resembles the Cv_inter_nn method:
Cv_inter_cubic-cubic interpolation.
The function cvresize changes the image src size to get the same sizes as DST. If you set ROI, the function will support the ROI as usual.
/****************************************************************************************************/
Believe that friends who have used OpenCV know how to use this function. Below according to my own understanding, with VC + + to achieve image scaling algorithm, I hope you can understand the principle of image scaling algorithm.
1th nearest neighbor interpolation
The simplest image scaling algorithm is the nearest neighbor interpolation. As the name implies, the pixel value of each point of the target image is set to its nearest point in the source image. Assuming that the width and height of the source image are W0 and H0 respectively, the width and height of the scaled target image are W1 and H1 respectively, then the ratio is float FW = float (w0)/w1; Float fh = float (H0)/h1; The (x, y) point coordinates in the target image correspond to (x0, y0) points in the source image. Where: x0 = Int (X*FW), y0 = Int (Y*FH).
Example 1: Now scale a 670*503 BMP image to 200*160, with the following code and effects.
void ResizeNear01 (CImage &src, CImage &dst)
{
int w0 = src. GetWidth ();
int H0 = src. GetHeight ();
int w1 = DST. GetWidth ();
int H1 = DST. GetHeight ();
float FW = Float (w0)/W1;
Float fh = float (H0)/H1;
int x0, y0;
for (int y=0; y{
y0 = Int (y * fh);
for (int x=0; x<w1; x + +)
{
x0 = Int (x * fw);
Dst. SetPixel (x, y, src.) GetPixel (x0, y0));
}
}
}
Analysis: For this program, we will execute the statement of this ResizeNear01 function with a for loop, so that it executes 100 times to see how fast it is.
#include <time.h>
void Cresizedemodlg::onbnclickedbutton1 ()
{
Todo:add your control notification handler code here
CImage SRC, DST;
Src. Load (L "d:\\1.bmp");
Dst. Create (200, 160, 24);
clock_t start = clock ();
for (int i=0; i<100; i++)
{
ResizeNear01 (SRC, DST);
}
float end = float (Clock ()-start)/clocks_per_sec;
CString str;
Str. Format (L "%6.2f", end);
MessageBox (str);
Dst. Save (L "d:\\rs.jpg");
}
It takes 20.59 seconds for the program to execute, an average of 0.2 seconds at a time, and it looks as if the speed is low because of the complexity of the time. The size of the target image is 200*160.
Example 2: The algorithm in Example 1 can improve the speed where there are two. First, because the nearest neighbor interpolation algorithm in the source image of the coordinates are fixed, you can each target each x and each y corresponding value through a cycle first to find out, and then into a double loop. Second, the use of pointers is more efficient if the use of GetPixel and SetPixel provided by CImage is time-consuming.
Optimized nearest neighbor interpolation algorithm
void ResizeNear02 (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 *arr_x = new INT[W1];
int *arr_y = new INT[H1];
for (int y=0; y{
Arr_y[y] = int (Y*FH);
}
for (int x=0; x<w1; x + +)
{
ARR_X[X] = int (X*FW);
}
byte* PSRC = (byte*) src. GetBits ();
byte* pDst = (byte*) DST. GetBits ();
byte* P0, *P1;
for (int y=0; y{
P0 = PSRC + pitch0 * Arr_y[y];
P1 = pDst + pitch1 * y;
for (int x=0; x<w1; x + +)
{
Dst. SetPixel (x, y, src.) GetPixel (Arr_x[x], arr_y[y]));
memcpy (p1 + 3*x, P0 + arr_x[x]*3, 3);
}
}
delete []arr_x;
delete []arr_y;
}
The same implementation of the test program in Example 1, so that ResizeNear02 also loop 100 times, on my machine to test the result is 0.05 seconds, the speed of 400 times times, this is a very exciting thing ah.
I contact image processing although it is almost a year, but still novice, hope that you are more correct.
2nd bilinear interpolation
bilinear interpolation as the default image scaling algorithm used in OPENCV, its effect and speed are good. And the effect is relatively stable, computational complexity is not too high. I read a lot of online algorithms, I did not see too understand, the following is from the Internet to find the bilinear interpolation algorithm explained.
"In the bilinear interpolation algorithm of image, the newly created pixel value in the target image is calculated by the weighted average of the value of 4 neighboring pixels of the location of the source image in the 2*2 area near it." The bilinear interpolation algorithm enlarges the image quality and does not occur in the case of discontinuous pixel values. However, the sub-algorithm has the properties of low-pass filter, so that the high-frequency component is damaged, so the image contour may become blurred to some extent. ”
below or according to my own understanding to continue to tell, I believe that the reader has a lot of experts, I hope readers can give me guidance, so I can understand some more.
bilinear interpolation algorithms are similar to nearest neighbor interpolation algorithms. In the nearest neighbor interpolation algorithm, a point (x, y) in the target image is the nearest point (x0, y0) in the de-source image. The point in the target image (x, y) corresponds to the point in the source image (x0 ', y0 '), x0 ', y0 ' is probably not an integer, but a decimal, and the nearest neighbor interpolation algorithm is to find its adjacent integer value (int (x0 ' +0.5f), int (y0 ' +0.5f)) (not rounded in the previous article). We now look for the four points next to the location of x0 ', Y0 ', based on the relationship between these four points and (x0 ', y0 ') distance to calculate the pixel values in the target image (x, y). The algorithm is described as follows:
(1) Calculate the ratio between the source image and the target image width and height
W0: Indicates the width of the source image
H0: Indicates the height of the source image
W1: Indicates the width of the target image
H1: Indicates the 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 point coordinates in the desired source image are (x1, y1) (x1, y2) (x2, y1) (X2,y2)
(3) Calculate the weight ratio of four points around
As shown above,
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 (coordinates) to represent the coordinate value that gets this point:
Value (x0,y0) = value (x2,y2) *s1+value (x1,y2) *s2+value (x1,y1) *s3+value (x2,y1) *s4;
If the above operation is not clear enough, this can be asked.
We first ask for the pixel values (x0, y1) and (X0,y2).
then 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 to a point, the greater the weight, so take its difference from 1.
Float value (x0,y0) = value (x0,y1) *fy2 + value (x0,y2) *fy1;
Validation is the same as the formula above.
(4) After the value is evaluated, it can be filled to the target image.
To make it easier for people to understand, we use GetPixel and SetPixel for value and assignment.
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 670*503 size picture to 200*160. After testing, the above algorithm executes once and takes about 0.5 seconds, which can be said to be very slow. If you scale it to a 2000*1600-sized picture, it takes 50 seconds at a time. This is very scary, if we do a Photoshop-like software now, users want to expand it a few times, but wait 50 seconds, it is estimated that users have no patience to use your software.
Let's analyze how the program can be optimized. After each step of the optimization algorithm, we use the same procedure and procedure above to test, to observe the elapsed time.
(1) Change function call to pointer operation as 1th section. (After this step, the program only needs about 0.2 seconds, the speed increased by 250 times times, haha.) )
(2) The calculation of the x0,y0 coordinates is extracted to the loop, because the x-coordinate of the second loop is repeated every time, and is repeated. (It still takes about 0.2 seconds.) We let it cycle 100 times, then compare the time spent. Using the ResizeLinear02 method takes 19.6 seconds, and using the ResizeLinear02 method is 17.4 seconds, which seems to work. )
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;
}