图像处理-放大和缩小
個人博客:http://www.chenjianqu.com/
原文鏈接:http://www.chenjianqu.com/show-7.html
在計算機圖像處理和計算機圖形學中,圖像縮放(image scaling)是指對數字圖像的大小進行調整的過程。圖像縮放是一種非平凡的過程,需要在處理效率以及結果的平滑度(smoothness)和清晰度(sharpness)上做一個權衡。當一個圖像的大小增加之后,組成圖像的像素的可見度將會變得更高,從而使得圖像表現得“軟”。相反地,縮小一個圖像將會增強它的平滑度和清晰度。
?
圖像縮小
??圖像縮小是通過減少像素個數來實現的,因此要根據縮小的尺寸,從原圖像中選擇合適的像素點,使圖像縮小后可以保持原有圖像的特征。圖像縮小的算法有很多,這里主要介紹和實現兩種算法:等間隔采樣、局部均值。
?
基于等間隔采樣的圖像縮小算法
??等間隔采樣其實就是在原圖中每間隔一定的距離取像素點放到輸出圖像上。設原圖的大小為W*H,寬度和長度的縮小因子分別為看k1和k2,那么采樣間隔為:W/k1,W/k2.也就是說在原圖的水平方向每隔W/k1,在垂直方向每隔W/k2取一個像素。長和寬的縮小因子k1和k2相等時,圖像時等比例縮小,不等時是不等比例縮小,縮小圖像的長和寬的比例會發生變化。
算法的實現步驟:
(1)計算采樣間隔
????設原圖的大小為W*H,將其放大(縮小)為(k1*W)*(K2*H),則采樣區間為
????ii=1/k1;??????
????jj=1/k2;
????當k1==k2時為等比例縮小;當k1!=k2時為不等比例放大(縮小);當k1<1 && k2<1時為圖片縮小,k1>1 && k2>1時圖片放大。
(2)求出放大(縮小)后的圖像
????設原圖為F(x,y)(i=1,2,?……W; j=1,2,……H),放大(縮小)的圖像為G(x,y)(x=1,2,?……M; y=1,2,……N,其中M=W*k1,N=H*k2),則有?G(x,y) = f(ii*x, jj*y)
?
代碼實現:
//圖像縮小—等間隔采樣 參數:原圖 x軸縮放比例 y軸縮放比例 Mat ZoomOutEqualInterval(Mat& src, double x_k,double y_k) {Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));x_k = 1 / x_k;//將縮小率轉換為采樣間隔y_k = 1 / y_k;cout << "dst x y:" << dst.cols << " " << dst.rows << endl;cout << "src x y:" <<src.cols << " " << src.rows << endl;if (src.channels() == 3){for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){int x = j * x_k + 0.5;int y = i * y_k + 0.5;if (x >= src.cols)x = src.cols - 1;if (y >= src.rows)y = src.rows - 1;dst.at<Vec3b>(i, j) = src.at<Vec3b>(y, x);}}}return dst; }縮小一倍的效果如下:
?
基于局部均值的圖像縮小算法
算法描述
??等間隔采樣的縮小方法實現簡單,但是原圖像中未被選中的像素信息會在縮小后的圖像中丟失。局部均值的圖像縮小方法對其進行了改進。在求縮小圖像的像素時,不僅僅單純的取在原圖像中的采樣點像素,而是以相鄰的兩個采樣點為分割,將原圖像分成一個個的子塊。縮小圖像的像素取相應子塊像素的均值。
根據局部均值縮小的原理:g11 = (f11 + f12 + f21 + f22 ) / 4
代碼實現:?
//圖像縮小—局部均值采樣 參數:原圖 x軸縮放比例 y軸縮放比例 Mat ZoomOutLocalMean(Mat& src, double x_k, double y_k) {Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));x_k = 1 / x_k;//將縮小率轉換為采樣間隔y_k = 1 / y_k;cout << "dst x y:" << dst.cols << " " << dst.rows << endl;cout << "src x y:" << src.cols << " " << src.rows << endl;if (src.channels() == 3){for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){int j_start = (j-1) * x_k+1;if (j_start < 0)j_start = 0;int j_end = j* x_k;if (j_end >=src.cols )j_end = src.cols;int i_start= (i-1) * y_k +1;if (i_start < 0)i_start = 0;int i_end = i * y_k;if (i_end >= src.rows)i_end = src.rows;int pix[3] = { 0,0,0 };int count = (j_end - j_start)*(i_end - i_start);for (int n = i_start; n < i_end; n++)for (int m = j_start; m < j_end; m++) {pix[0] = src.at<Vec3b>(n, m)[0];pix[1] = src.at<Vec3b>(n, m)[1];pix[2] = src.at<Vec3b>(n, m)[2];}if (count != 0) {Vec3b v(pix[0] / count, pix[1] / count, pix[2] / count);dst.at<Vec3b>(i, j) = v;}elsedst.at<Vec3b>(i, j) = src.at<Vec3b>(i,j);}}}return dst; }效果圖:
?
?
圖像放大
??要將該圖像放大兩倍,可以有很多種算法,最簡單的方法為鄰域插值,即將每一個原像素原封不動地復制映射到擴展后對應四個像素中:
? ????這種方法在放大圖像的同時保留了所有的原圖像的所有信息,但是會產生鋸齒現象。
雙線性插值的效果對于放大的圖像而言較領域插值來得平滑,但是卻使得圖像變得模糊而且仍然會有一部分鋸齒現象。雙三次插值更好比雙線性插值更好。這里只實現最近鄰插值和雙線性插值。
?
最鄰近插值
原理
????原始圖像:I(x,y),?輸出圖像:F(x,y),則放大過程可轉換為I(x,y)=F(int(c1*i),int(c2*j)),其中c1=1/y軸放大倍數,c2=1/x軸放大倍數系數乘以原坐標值得到的坐標值可能含有小數,所以,我們必須采取一定方法(如:四舍五入,我們這里直接去掉小數部分)來舍棄小數部分,從而取得整數表示的像素點坐標,?該過程即為最鄰近插值方法。
?
優缺點
??最鄰近插值簡單且直觀,但得到的圖像質量不高,特別在圖像放大后可能產生明顯鋸齒。
代碼實現://圖像放大-最近鄰插值法
Mat ZoomInNearestNeighborInterpolation(Mat& src, double x_k, double y_k) {Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));x_k = 1 / x_k;y_k = 1 / y_k;if (src.channels() == 3){for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++)dst.at<Vec3b>(i, j) = src.at<Vec3b>(y_k*i, x_k*j);}}return dst; }當x軸和y軸的放大系數均為2時,運行的效果為:
??
雙線性插值
原理
? ? ? 如圖所示,最鄰近插值是當求得p0后,直接找其鄰近的點p1, p2, p3, p4中的一個的像素值作為目標點的像素;而雙線性插值,則是根據p0點與周圍4點(p1, p2, p3, p4)距離關系計算目標點的像素值。
通過計算得到的原始點為p0(x0, y0),則其4周的點分別為:
x0的可能取值為:sx1 = (int)x0, sx2 = sx1 + 1
y0的可能取值為:sy1 = (int)y0, sy2 = sy1 + 1
設:
s1 = y0 – sy1
s2 = sx2 – x0
s3 = 1.0 – s1
s4 = 1.0 – s2
假設p1, p2, p3, p4的像素值分別為v1, v2, v3, v4,
則雙線性插值計算p0點像素值v0公式為:
v0 = v1*s1*s4 + v2*s1*s2 + v3*s2*s3 + v4*s3*s4
?
優缺點
??雙線性內插值法計算量大,但縮放后圖像質量高,不會出現像素值不連續的的情況。
? 雙線性插值具有低通濾波器的性質,使高頻分量受損,所以可能會使圖像輪廓在一定程度上變得模糊。
?
代碼實現:
//圖像放大-雙線性插值法 Mat ZoomInBilinearInterpolation(Mat& src, double x_k, double y_k) {Mat dst(src.rows*y_k, src.cols*x_k, src.type(), Scalar(0));x_k = 1 / x_k;y_k = 1 / y_k;if (src.channels() == 3){for (int i = 0; i < dst.rows; i++){for (int j = 0; j < dst.cols; j++){double x0 = x_k * j;double y0 = y_k * i;int x1 = int(x0);int y1 = int(y0);double s1 = y0 - y1;double s4 = x0 - x1;double s2 = 1 - s4;double s3 = 1 - s1;if (x1 >= src.cols - 1)x1 = src.cols - 2;if (y1 >= src.rows - 1)y1 = src.rows - 2;dst.at<Vec3b>(i, j) = src.at<Vec3b>(y1, x1)*s1*s4 + src.at<Vec3b>(y1, x1 + 1)*s1*s2 + src.at<Vec3b>(y1 + 1, x1 + 1)*s2*s3 + src.at<Vec3b>(y1 + 1, x1)*s3*s4;}}}return dst; }效果圖:
?
三次卷積法
原理
??雙立方插值算法與雙線性插值算法類似,對于放大后未知的像素點P,將對其影響的范圍擴大到鄰近的16個像素點,依據對P點的遠近影響進行插值計算,因P點的像素值信息來自16個鄰近點,所以可得到較細致的影像,不過速度比較慢。
??不過雙立方插值算法與雙線性插值算法的本質區別不僅在于擴大了影響點的范圍,還采用高級的插值算法,如圖所示:
?????要求A,B兩點之間e點的值,需要利用A,B周圍A-1,A,B,B 1四個點的像素值,通過某種非線性的計算,得到光滑的曲線,從而算出e點的值來。
????所謂“雙”或者叫“二次”的意思就是在計算了橫向插值影響的基礎上,把上述運算拓展到二維空間,再計算縱向插值影響的意思。
? ????雙立方插值算法能夠得到相對清晰的畫面質量,不過計算量也變大。該算法在現在的眾多圖像處理軟件中最為常用,比如Photoshop,After Effects,Avid,Final Cut Pro等。
? ????為了得到更好的圖像質量,在以上的基礎上,許多新的算法不斷涌現,它們使用了更加復雜的改進的插值方式。譬如B樣條(B-SPline),?米切爾(Mitchell)等插值算法,它們的目的是使插值的曲線顯得更平滑,圖像邊緣的表現更加完美。
?
自適應樣條插值極其增強技術(?S-Spline & S-Spline XL)
??與上述經典的插值方法最大的區別在于,?S-Spline?采用了一種自適應技術,那些傳統的方法總是依據周圍的像素點來求未知點的色彩值,也就是說需要求解的色彩值僅僅依靠該像素點在圖像中的位置,而非實際的圖像的像素信息,而自適應樣條算法還會考慮實際圖像的像素信息。?實驗表明,經過?S-Spline?算法得到的圖像效果要優于雙立方插值算法。
? ????現在?S-Spline?算法又出現了增強版?S-Spline XL,新版本的?S-Spline XL?算法較?S-Spline?而言畫面的銳度得到進一步增強,物體的輪廓更加清晰,邊緣的鋸齒現象大大減弱,圖像感受更加自然。
?
?
參考文獻
[1]博客園:淑月塵緣.?圖像縮放算法?.
https://www.cnblogs.com/sycy/p/4743620.html . 2015-08-19
[2]CSDN博客:rainbowbirds_aes.圖像放大算法.
https://blog.csdn.net/rainbowbirds_aes/article/details/83114266. 2018.10.17
[3]博客園:Brook_icv. OpenCV2:等間隔采樣和局部均值的圖像縮小.
http://www.cnblogs.com/wangguchangqing/p/4011892.html. 2014.10.08
[3]韓九強,楊磊.數據圖像處理-基于XAVIS組態軟件.西安交通大學出版社.2018.8
?
?
?
?
?
總結
以上是生活随笔為你收集整理的图像处理-放大和缩小的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ix Chariot测试路由器流程
- 下一篇: AI也能作曲?谁来定义AI的freest