For a long time do not knock code, feel a leisure to forget a lot. Thinking about some image algorithms to achieve their own again, on the one hand to deepen the learning and understanding of the algorithm, on the other hand can practice coding ability. It’s good for me, too.

Whatever it is, dig this hole first. Fill in the code when you can.

The following image processing in the common three interpolation algorithm to achieve. Let me explain what interpolation is. As usual, let’s see what Wikipedia says.

In the field of numerical analysis in mathematics, interpolation or interpolation is a process or method of deducing new data points within a range from known discrete data points.


450px-Splined_epitrochoid.svg.png

The interpolation of a set of discrete data points in an epitaxial manner. The actual known data points in the curve are red; The blue curve connecting them is called interpolation.

Here’s another example:

X1 = 1, y1 = 3, x2 = 3, y2 = 7, x3 = 5, y3 = 24Copy the code

So you can see, you can think of interpolation this way, in a function, the independent variables are discrete and spaced, and interpolation is just inserting new variables between the intervals of the independent variables, and then solving for the new values of the independent variables. What does that do? As you can see from the figure above, you can use interpolation to fit the curve, and the blue ones are the dense points of insertion.

However, the application of image processing can be reflected in the image of the zoom above. For example, to enlarge an image on the level of pixels is to insert new pixels between pixels to increase the resolution of the image.

There are many different interpolation algorithms. For details, see Wikipedia interpolation

Here the realization of image processing is more commonly used three: the nearest neighbor area difference, bilinear interpolation, bicubic interpolation

Nearest neighbor interpolation

Nearest Neighbor Interpolation

The nearest neighbor field is the simplest of the three interpolation methods. The principle is to select the pixel nearest to the inserted pixel (x+u, y+ V) and replace the inserted pixel with the gray value of its pixel. Speaking of distance, review the three spatial distances between pixels.

For pixels P, Q and z, there are coordinates (x,y), (s,t) and (u,v) respectively, if

(1) D(p,q) ≥ 0 (if and only if p=q, D(p,q)=0)

(2) D(p,q) = D(q,p)

(3) D(p,z) ≤ D(p,q) + D(q,z)

D is called a distance function or measure


Euclidean distance. GIF

D4 Distance (city distance)




D4. GIF

D8 distance (checkerboard distance)




The D8 distance. GIF

Here the code is implemented using Euclidean distance

// Find the 4 pixels in the original image according to the pixels of the target image (floating point coordinates), and take the original pixel value nearest to the pixel as the value of the point. #include<opencv2/opencv.hpp> #include<cassert> namespace mycv { void nearestIntertoplation(cv::Mat& src, cv::Mat& dst, const int rows, const int cols); }//mycv double distance(const double x1, const double y1, const double x2, const double y2); Int main() {CV ::Mat img = CV ::imread("lena.jpg", 0); if (img.empty()) return -1; cv::Mat dst; mycv::nearestIntertoplation(img, dst, 600, 600); cv::imshow("img", img); cv::imshow("dst", dst); cv::waitKey(0); return 0; return 0; }//main void mycv::nearestIntertoplation(cv::Mat& src, cv::Mat& dst, const int rows, Const int cols) {const double scale_row = static_cast<double>(src.rows)/rows; const double scale_col = static_cast<double>(src.rows) / cols; DST = CV ::Mat(rows, cols, src.type()); assert(src.channels() == 1 && dst.channels() == 1); for (int i = 0; i < rows; For (int j = 0; j < cols; Double y = (I + 0.5) * scale_row + 0.5; double y = (I + 0.5) * scale_row + 0.5; Double x = (j + 0.5) * scale_col + 0.5; int x1 = static_cast<int>(x); If (x1 >= (src.cols-2)) x1 = src.cols-2; Int x2 = x1 + 1; int y1 = static_cast<int>(y); //row = y if (y1 >= (src.rows-2)) y1 = src.rows-2; int y2 = y1 + 1; // Find the 4 pixels in the original image according to the pixels of the target image (floating point coordinates), and take the nearest original pixel value as the value of this pixel. assert(0 < x2 && x2 < src.cols && 0 < y2 && y2 < src.rows); std::vector<double> dist(4); dist[0] = distance(x, y, x1, y1); dist[1] = distance(x, y, x2, y1); dist[2] = distance(x, y, x1, y2); dist[3] = distance(x, y, x2, y2); int min_val = dist[0]; int min_index = 0; for (int i = 1; i < dist.size(); ++i) if (min_val > dist[i]) { min_val = dist[i]; min_index = i; } switch (min_index) { case 0: dst.at<uchar>(i, j) = src.at<uchar>(y1, x1); break; case 1: dst.at<uchar>(i, j) = src.at<uchar>(y1, x2); break; case 2: dst.at<uchar>(i, j) = src.at<uchar>(y2, x1); break; case 3: dst.at<uchar>(i, j) = src.at<uchar>(y2, x2); break; default: assert(false); }} double distance(const double x1, const double y1, const double x2, const double y2) {return (x1 - x2)*(x1 - x2) + (y1-y2)*(y1-y2); // Return distance square}Copy the code

Bilinear interpolation

Bilinear interpolation, as the name suggests, is the result of linear interpolation in the x and y directions over a matrix of pixels. So let’s look at linear interpolation


Linear_interpolation.png

image.png

image.png

So that’s one dimension, so let’s look at bilinear interpolation in two dimensions


Bilinear_interpolation.png

First we interpolate linearly above the x direction to get R2 and R1




image.png

And then we interpolate linearly again in the y direction with R2,R1




image.png

If a coordinate system is selected such that the coordinates of the four known points of F are (0, 0), (0, 1), (1, 0), and (1, 1), then the interpolation formula can be simplified to


image.png

Matrix representation




image.png

The code implementation is posted below

#include<opencv2/opencv. HPP > #include<opencv2/core/matx. HPP > #include<cassert> namespace mycv {void bilinearIntertpolatioin(cv::Mat& src, cv::Mat& dst, const int rows, const int cols); }//mycv int main() { cv::Mat img = cv::imread("lena.jpg", 0); if (img.empty()) return -1; cv::Mat dst; mycv::bilinearIntertpolatioin(img, dst, 600, 600); cv::imshow("img", img); cv::imshow("dst", dst); cv::waitKey(0); return 0; }//main void mycv::bilinearIntertpolatioin(cv::Mat& src, cv::Mat& dst, const int rows, Const int cols) {const double scale_row = static_cast<double>(src.rows)/rows; const double scale_col = static_cast<double>(src.rows) / cols; DST = CV ::Mat(rows, cols, src.type()); assert(src.channels() == 1 && dst.channels() == 1); for(int i = 0; i < rows; For (int j = 0; j < cols; Double y = (I + 0.5) * scale_row + 0.5; double y = (I + 0.5) * scale_row + 0.5; Double x = (j + 0.5) * scale_col + 0.5; int x1 = static_cast<int>(x); If (x1 >= (src.cols-2)) x1 = src.cols-2; Int x2 = x1 + 1; int y1 = static_cast<int>(y); //row = y if (y1 >= (src.rows-2)) y1 = src.rows-2; int y2 = y1 + 1; assert(0 < x2 && x2 < src.cols && 0 < y2 && y2 < src.rows); / / interpolation formula, reference wikipedia matrix multiplication formula CV: https://zh.wikipedia.org/wiki/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC: Matx12d matx = {x2 - x, x - x1 }; cv::Matx22d matf = { static_cast<double>(src.at<uchar>(y1, x1)), static_cast<double>(src.at<uchar>(y2, x1)), static_cast<double>(src.at<uchar>(y1, x2)), static_cast<double>(src.at<uchar>(y2, x2)) }; cv::Matx21d maty = { y2 - y, y - y1 }; auto val = (matx * matf * maty); DST. At < uchar > (I, j) = val (0, 0); }}Copy the code

Bicubic interpolation

Bicubic interpolation is a little bit more complicated. Wikipedia

In the mathematical branch of numerical analysis, Bicubic interpolation is the most commonly used interpolation method in two dimensional space. In this method, the value of the function f at the point (x, y) can be obtained by a weighted average of the nearest sixteen sampling points in a rectangular grid, where two polynomial interpolation cubic functions are required, one in each direction

Bicubic interpolation calculation formula


image.png


namely




image.png

So this a(I, j) is the weighting factor, so the key is to figure it out.

The formula for solving the weighting coefficient is as follows


image.png

The code for solving the strengthening coefficient is as follows

std::vector<double> mycv::getW(double coor, double a) { std::vector<double> w(4); int base = static_cast<int>(coor); Double e = coor-static_cast <double>(base); STD ::vector<double> TMP (4); Deposit / / | | x in the formula 1 < | | x < < = 1, 2 four values / / 4 x 4 to 16 points, so the TMP [0] and TMP distance [4], value within the range of [1, 2] TMP + e [0] = 1.0; // 1 < x < 2 tmp[1] = e; //x <= 1 TMP [2] = 1.0-e; // x <= 1 TMP [3] = 2.0-e; / / 1 < < x 2 w / / / / in accordance with the bicubic formula coefficient x < = 1 w [1] = (a + 2.0) * STD: : abs (STD: : pow (TMP [1]. 3) - (a + 3.0) * STD: : abs (STD: : pow (TMP [1], 2)) + 1; W [2] = (a + 2.0) * STD: : abs (STD: : pow (TMP [2], 3)) - (a + 3.0) * STD: : abs (STD: : pow (TMP [2], 2)) + 1; / / 1 < < x 2 w [0] = a * STD: : abs (STD: : pow (TMP [0], 3)) - 5.0 * a * STD: : abs (STD: : pow (TMP [0]. 2)) + 8.0*a* STD ::abs(TMP [0]) - 4.0*a; W [3] = a * STD: : abs (STD: : pow (TMP [3], 3)) - 5.0 * a * STD: : abs (STD: : pow (TMP [3], 2)) + 8.0 * a * STD: : abs (TMP) [3] - 4.0 * a; return w; }Copy the code

After the solution, wx and WY are the 16 coefficients of 4×4, which match the 16 points of 4×4 used for interpolation. Key code for interpolation steps

STD ::vector< STD ::vector<int> > src_arr = {{src.at<uchar>(rr-1, cc-1), src.at<uchar>(rr, cc - 1), src.at<uchar>(rr + 1, cc - 1), src.at<uchar>(rr + 2, cc - 1)}, { src.at<uchar>(rr - 1, cc), src.at<uchar>(rr, cc), src.at<uchar>(rr + 1, cc), src.at<uchar>(rr + 2, cc)}, { src.at<uchar>(rr - 1, cc + 1), src.at<uchar>(rr, cc + 1), src.at<uchar>(rr + 1, cc + 1), src.at<uchar>(rr + 2, cc + 1)}, { src.at<uchar>(rr - 1, cc + 2), src.at<uchar>(rr, cc + 2), src.at<uchar>(rr + 1, cc + 2), src.at<uchar>(rr + 2, cc + 2)} }; for(int p = 0; p < 3; ++p) for (int q = 0; q < 3; ++q) { //val(p, q) = w(p,q) * src(p, q) val += wr[p] * wc[q] * static_cast<double>(src_arr[p][q]); } assert(i < dst.rows && j < dst.cols); dst.at<uchar>(i, j) = static_cast<int>(val);Copy the code

The complete code is shown below

#include<opencv2/opencv. HPP > #include<iostream> #include<vector> #include<cassert> namespace mycv {void bicubicInsterpolation(cv::Mat& src, cv::Mat& dst, const int rows, const int cols); STD ::vector<double> getW(double coor, double a = -0.5); //mycv int main(void) {CV ::Mat img = CV ::imread("lena.jpg", 0); if (img.empty()) return -1; cv::Mat dst; mycv::bicubicInsterpolation(img, dst, 600, 600); cv::imshow("img", img); cv::imshow("dst", dst); cv::waitKey(0); return 0; }//main void mycv::bicubicInsterpolation(cv::Mat& src, cv::Mat& dst, const int rows, const int cols) { dst = cv::Mat(rows, cols, src.type(), cv::Scalar::all(0)); Double row_scale = static_cast<double>(src.rows)/rows; double col_scale = static_cast<double>(src.cols) / cols; Switch (src.Channels ()) {case 1:// Grayscale for(int I = 2; i < dst.rows - 2; ++i) for (int j = 2; j < dst.cols - 2; R = static_cast<double>(I * row_scale); double c = static_cast<double>(j * col_scale); If (r < 1.0) r += 1.0; If (c < 1.0) c += 1.0; std::vector<double> wr = mycv::getW( r); std::vector<double> wc = mycv::getW( c); // Double val = 0; int cc = static_cast<int>(c); int rr = static_cast<int>(r); If (cc > src.cols-3) {cc = src.cols-3; } if (rr > src.rows - 3) rr = src.rows - 3; assert(0 <= (rr - 1) && 0 <= (cc - 1) && (rr + 2) < src.rows && (cc + 2) < src.cols); STD ::vector< STD ::vector<int> > src_arr = {{src.at<uchar>(rr-1, cc-1), src.at<uchar>(rr, cc - 1), src.at<uchar>(rr + 1, cc - 1), src.at<uchar>(rr + 2, cc - 1)}, { src.at<uchar>(rr - 1, cc), src.at<uchar>(rr, cc), src.at<uchar>(rr + 1, cc), src.at<uchar>(rr + 2, cc)}, { src.at<uchar>(rr - 1, cc + 1), src.at<uchar>(rr, cc + 1), src.at<uchar>(rr + 1, cc + 1), src.at<uchar>(rr + 2, cc + 1)}, { src.at<uchar>(rr - 1, cc + 2), src.at<uchar>(rr, cc + 2), src.at<uchar>(rr + 1, cc + 2), src.at<uchar>(rr + 2, cc + 2)} }; for(int p = 0; p < 3; ++p) for (int q = 0; q < 3; ++q) { //val(p, q) = w(p,q) * src(p, q) val += wr[p] * wc[q] * static_cast<double>(src_arr[p][q]); } assert(i < dst.rows && j < dst.cols); dst.at<uchar>(i, j) = static_cast<int>(val); } break; Case 3:// Color (same principle with two channels) break; default: break; } } std::vector<double> mycv::getW(double coor, double a) { std::vector<double> w(4); int base = static_cast<int>(coor); Double e = coor-static_cast <double>(base); STD ::vector<double> TMP (4); Deposit / / | | x in the formula 1 < | | x < < = 1, 2 four values / / 4 x 4 to 16 points, so the TMP [0] and TMP distance [4], value within the range of [1, 2] TMP + e [0] = 1.0; // 1 < x < 2 tmp[1] = e; //x <= 1 TMP [2] = 1.0-e; // x <= 1 TMP [3] = 2.0-e; / / 1 < < x 2 w / / / / in accordance with the bicubic formula coefficient x < = 1 w [1] = (a + 2.0) * STD: : abs (STD: : pow (TMP [1]. 3) - (a + 3.0) * STD: : abs (STD: : pow (TMP [1], 2)) + 1; W [2] = (a + 2.0) * STD: : abs (STD: : pow (TMP [2], 3)) - (a + 3.0) * STD: : abs (STD: : pow (TMP [2], 2)) + 1; / / 1 < < x 2 w [0] = a * STD: : abs (STD: : pow (TMP [0], 3)) - 5.0 * a * STD: : abs (STD: : pow (TMP [0]. 2)) + 8.0*a* STD ::abs(TMP [0]) - 4.0*a; W [3] = a * STD: : abs (STD: : pow (TMP [3], 3)) - 5.0 * a * STD: : abs (STD: : pow (TMP [3], 2)) + 8.0 * a * STD: : abs (TMP) [3] - 4.0 * a; return w; }Copy the code

The calculation of cubic interpolation code is particularly large, and the code is not optimized, the speed is slow. But feel readability or ok, easy to understand it.