您好,登錄后才能下訂單哦!
本篇內容主要講解“C++ OpenCV如何實現圖像雙三次插值算法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“C++ OpenCV如何實現圖像雙三次插值算法”吧!
首先是原理部分。圖像雙三次插值的原理,就是目標圖像的每一個像素都是由原圖上相對應點周圍的4x4=16個像素經過加權之后再相加得到的。這里的加權用到的就是三次函數,這也是圖像雙三次插值算法名稱的由來(個人猜測)。用到的三次函數如下圖所示:
最關鍵的問題是,這個三次函數的輸入和輸出分別代表啥。簡單來說輸入就是原圖對應點周圍相對于這點的4x4大小區域的坐標值,大小在0~2之間,輸出就是這些點橫坐標或者縱坐標的權重。4個橫坐標、4個縱坐標,對應相乘就是4x4大小的權重矩陣,然后使用此權重矩陣對原圖相對應的區域進行相乘并相加就可以得到目標圖點的像素。
下圖可以幫助大家更好地理解
首先,u和v是什么呢?舉一個例子,對于一幅100x100的灰度圖像,要將其放大到500x500,那么其縮放因子sx=500/100=5,sy=500/100=5。現在目標圖像是500x500,需要用原圖的100x100個像素值來填滿這500x500個空,根據src_x=i/sx和src_y=j/sy可以得到目標像素坐標(i,j)所對應的原圖像素坐標(src_x, src_y),這個src_x和src_y的小數部分就是上圖中的u和v。
理解了u和v,就可以利用u和v來計算雙三次插值算法的權重了。上面說了三次函數的輸入是原圖對應點周圍相對于這點的4x4大小區域的坐標值,對于上面這幅圖而言,橫坐標有四個輸入,分別是1+u,u,1-u,2-u;縱坐標也有四個輸入,分別是1+v,v,1-v,2-v,根據三次函數算出權重之后兩兩相乘就是對應的4x4大小的權重矩陣。
知道了怎么求權重矩陣之后,就可以遍歷整幅圖像進行插值了。下面是基于自己對原理的理解編寫的c++ opencv代碼,代碼沒有做優化,但是能夠讓大家直觀地理解圖像雙三次插值算法。
前面說了權重矩陣就是橫坐標的4個輸出和縱坐標的4個輸出相乘,因此只需要求出橫坐標和縱坐標相對應的8個輸出值就行了。
代碼如下:
std::vector<double> getWeight(double c, double a = 0.5) { //c就是u和v,橫坐標和縱坐標的輸出計算方式一樣 std::vector<double> temp(4); temp[0] = 1 + c; temp[1] = c; temp[2] = 1 - c; temp[3] = 2 - c; //y(x) = (a+2)|x|*|x|*|x| - (a+3)|x|*|x| + 1 |x|<=1 //y(x) = a|x|*|x|*|x| - 5a|x|*|x| + 8a|x| - 4a 1<|x|<2 std::vector<double> weight(4); weight[0] = (a * pow(abs(temp[0]), 3) - 5 * a * pow(abs(temp[0]), 2) + 8 * a * abs(temp[0]) - 4 * a); weight[1] = (a + 2) * pow(abs(temp[1]), 3) - (a + 3) * pow(abs(temp[1]), 2) + 1; weight[2] = (a + 2) * pow(abs(temp[2]), 3) - (a + 3) * pow(abs(temp[2]), 2) + 1; weight[3] = (a * pow(abs(temp[3]), 3) - 5 * a * pow(abs(temp[3]), 2) + 8 * a * abs(temp[3]) - 4 * a); return weight; }
代碼如下:
void bicubic(cv::Mat& src, cv::Mat& dst, int dst_rows, int dst_cols) { dst.create(dst_rows, dst_cols, src.type()); double sy = static_cast<double>(dst_rows) / static_cast<double>(src.rows); double sx = static_cast<double>(dst_cols) / static_cast<double>(src.cols); cv::Mat border; cv::copyMakeBorder(src, border, 1, 1, 1, 1, cv::BORDER_REFLECT_101); //處理灰度圖 if (src.channels() == 1) { for (int i = 1; i < dst_rows + 1; ++i) { int src_y = (i + 0.5) / sy - 0.5; //做了幾何中心對齊 if (src_y < 0) src_y = 0; if (src_y > src.rows - 1) src_y = src.rows - 1; src_y += 1; //目標圖像點坐標對應原圖點坐標的4個縱坐標 int i1 = std::floor(src_y); int i2 = std::ceil(src_y); int i0 = i1 - 1; int i3 = i2 + 1; double u = src_y - static_cast<int64>(i1); std::vector<double> weight_x = getWeight(u); for (int j = 1; j < dst_cols + 1; ++j) { int src_x = (j + 0.5) / sy - 0.5; if (src_x < 0) src_x = 0; if (src_x > src.rows - 1) src_x = src.rows - 1; src_x += 1; //目標圖像點坐標對應原圖點坐標的4個橫坐標 int j1 = std::floor(src_x); int j2 = std::ceil(src_x); int j0 = j1 - 1; int j3 = j2 + 1; double v = src_x - static_cast<int64>(j1); std::vector<double> weight_y = getWeight(v); //目標點像素對應原圖點像素周圍4x4區域的加權計算(插值) double pix = weight_x[0] * weight_y[0] * border.at<uchar>(i0, j0) + weight_x[1] * weight_y[0] * border.at<uchar>(i0, j1) + weight_x[2] * weight_y[0] * border.at<uchar>(i0, j2) + weight_x[3] * weight_y[0] * border.at<uchar>(i0, j3) + weight_x[0] * weight_y[1] * border.at<uchar>(i1, j0) + weight_x[1] * weight_y[1] * border.at<uchar>(i1, j1) + weight_x[2] * weight_y[1] * border.at<uchar>(i1, j2) + weight_x[3] * weight_y[1] * border.at<uchar>(i1, j3) + weight_x[0] * weight_y[2] * border.at<uchar>(i2, j0) + weight_x[1] * weight_y[2] * border.at<uchar>(i2, j1) + weight_x[2] * weight_y[2] * border.at<uchar>(i2, j2) + weight_x[3] * weight_y[2] * border.at<uchar>(i2, j3) + weight_x[0] * weight_y[3] * border.at<uchar>(i3, j0) + weight_x[1] * weight_y[3] * border.at<uchar>(i3, j1) + weight_x[2] * weight_y[3] * border.at<uchar>(i3, j2) + weight_x[3] * weight_y[3] * border.at<uchar>(i3, j3); if (pix < 0) pix = 0; if (pix > 255)pix = 255; dst.at<uchar>(i - 1, j - 1) = static_cast<uchar>(pix); } } } //處理彩色圖像 else if (src.channels() == 3) { for (int i = 1; i < dst_rows + 1; ++i) { int src_y = (i + 0.5) / sy - 0.5; if (src_y < 0) src_y = 0; if (src_y > src.rows - 1) src_y = src.rows - 1; src_y += 1; int i1 = std::floor(src_y); int i2 = std::ceil(src_y); int i0 = i1 - 1; int i3 = i2 + 1; double u = src_y - static_cast<int64>(i1); std::vector<double> weight_y = getWeight(u); for (int j = 1; j < dst_cols + 1; ++j) { int src_x = (j + 0.5) / sy - 0.5; if (src_x < 0) src_x = 0; if (src_x > src.rows - 1) src_x = src.rows - 1; src_x += 1; int j1 = std::floor(src_x); int j2 = std::ceil(src_x); int j0 = j1 - 1; int j3 = j2 + 1; double v = src_x - static_cast<int64>(j1); std::vector<double> weight_x = getWeight(v); cv::Vec3b pix; pix[0] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[0] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[0] + weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[0] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[0] + weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[0] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[0] + weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[0] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[0] + weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[0] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[0] + weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[0] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[0] + weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[0] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[0] + weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[0] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[0]; pix[1] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[1] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[1] + weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[1] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[1] + weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[1] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[1] + weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[1] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[1] + weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[1] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[1] + weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[1] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[1] + weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[1] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[1] + weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[1] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[1]; pix[2] = weight_x[0] * weight_y[0] * border.at<cv::Vec3b>(i0, j0)[2] + weight_x[1] * weight_y[0] * border.at<cv::Vec3b>(i0, j1)[2] + weight_x[2] * weight_y[0] * border.at<cv::Vec3b>(i0, j2)[2] + weight_x[3] * weight_y[0] * border.at<cv::Vec3b>(i0, j3)[2] + weight_x[0] * weight_y[1] * border.at<cv::Vec3b>(i1, j0)[2] + weight_x[1] * weight_y[1] * border.at<cv::Vec3b>(i1, j1)[2] + weight_x[2] * weight_y[1] * border.at<cv::Vec3b>(i1, j2)[2] + weight_x[3] * weight_y[1] * border.at<cv::Vec3b>(i1, j3)[2] + weight_x[0] * weight_y[2] * border.at<cv::Vec3b>(i2, j0)[2] + weight_x[1] * weight_y[2] * border.at<cv::Vec3b>(i2, j1)[2] + weight_x[2] * weight_y[2] * border.at<cv::Vec3b>(i2, j2)[2] + weight_x[3] * weight_y[2] * border.at<cv::Vec3b>(i2, j3)[2] + weight_x[0] * weight_y[3] * border.at<cv::Vec3b>(i3, j0)[2] + weight_x[1] * weight_y[3] * border.at<cv::Vec3b>(i3, j1)[2] + weight_x[2] * weight_y[3] * border.at<cv::Vec3b>(i3, j2)[2] + weight_x[3] * weight_y[3] * border.at<cv::Vec3b>(i3, j3)[2]; for (int i = 0; i < src.channels(); ++i) { if (pix[i] < 0) pix = 0; if (pix[i] > 255)pix = 255; } dst.at<cv::Vec3b>(i - 1, j - 1) = static_cast<cv::Vec3b>(pix); } } } }
int main() { cv::Mat src = cv::imread("C:\\Users\\Echo\\Pictures\\Saved Pictures\\bilateral.png"); cv::Mat dst; bicubic(src, dst, 309/0.5, 338/0.5); cv::imshow("gray", dst); cv::imshow("src", src); cv::waitKey(0); }
彩色圖像(放大兩倍)
到此,相信大家對“C++ OpenCV如何實現圖像雙三次插值算法”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。