圖像縮放是數(shù)字圖像處理中常用的技術(shù)之一。隨著數(shù)字媒體的普及,圖像縮放算法變得越來越重要。本文將探討圖像縮放的原理,著重介紹兩種常用的插值算法——最近鄰插值和雙線性插值,并提供對應(yīng)的代碼實(shí)現(xiàn)。我們將解釋這些算法的工作原理,以及如何選擇最適合您應(yīng)用場景的算法。 ? 開發(fā)中可能非專業(yè)的開發(fā)人員外,其他人不會對其涉獵,但是圖像的縮放這個話題確實(shí)值得我們學(xué)習(xí)和研究。 ? 圖像包含圖片和視頻,當(dāng)你看視頻時全屏變小窗,小窗變大屏等都用到了圖像縮放,就視頻而言,不同分辨率的切換也用到了縮放算法,比如,電影分辨率是 1080P,播放器的窗口大小是720P,則需要將電影畫面從1080P縮小到720P再播放。如果你點(diǎn)擊全屏播放,播放窗口變成了4K,則需要將電影畫面做放大處理,即放大到4K之后再播放。 ?
那圖像縮放算法都有哪些呢?
他們的原理是什么?
在Android開發(fā)中怎么使用
圖像縮放算法
圖像縮放算法可以分為兩類:插值算法和基于變換的算法。下面是一些常見的圖像縮放算法: ?
最近鄰插值算法(Nearest Neighbor Interpolation):最簡單的插值算法,對于每個縮放后的像素點(diǎn),選擇與其最近的原始像素點(diǎn)的值作為它的值。該算法容易實(shí)現(xiàn),但會導(dǎo)致圖像出現(xiàn)鋸齒狀的邊緣。
雙線性插值算法(Bilinear Interpolation):該算法在最近鄰插值算法的基礎(chǔ)上,加入了對相鄰四個像素點(diǎn)的加權(quán)平均,使得圖像邊緣更加平滑。
雙三次插值算法(Bicubic Interpolation):該算法在雙線性插值算法的基礎(chǔ)上,對相鄰16個像素點(diǎn)進(jìn)行加權(quán)平均,得到更加平滑的圖像。
Lanczos插值算法(LanczosInterpolation):該算法使用了一種卷積方法,通過對像素點(diǎn)周圍的采樣點(diǎn)進(jìn)行加權(quán)平均,得到縮放后像素點(diǎn)的值。該算法在保持圖像細(xì)節(jié)的同時,會對圖像進(jìn)行輕微模糊。
Sinc插值算法(SincInterpolation):該算法基于信號處理中的Sinc函數(shù),使用卷積方法對像素點(diǎn)進(jìn)行加權(quán)平均。該算法在保持圖像細(xì)節(jié)的同時,會對圖像進(jìn)行一定的模糊,但比Lanczos插值算法的模糊程度小。
基于變換的算法:除了插值算法,還有一些基于變換的算法,如雙線性變換、雙三次變換、圖像金字塔等。這些算法會對原始圖像進(jìn)行一定的變換,再進(jìn)行縮放。這些算法通常能夠產(chǎn)生更高質(zhì)量的縮放結(jié)果,但計(jì)算復(fù)雜度也更高。
這些算法已經(jīng)在行業(yè)中有較多的資料和使用文獻(xiàn),我這里簡單梳理一下他們的原理以及在Android中怎么使用這些算法。 ?
縮放的原理
? 首先要明確一點(diǎn)就是圖像的縮放就是將原圖像的已有像素經(jīng)過加權(quán)運(yùn)算得到目標(biāo)圖像的目標(biāo)像素。比如說,我們已有圖像是720P的分辨率,稱之為原圖像,我們需要放大到1080P,我們稱這個1080P圖像是目標(biāo)圖像。目標(biāo)圖像在寬度方向上放大了1920 / 1280 = 1.5倍,高度方向上也放大了1080 / 720 = 1.5倍。 ? 那怎么通過720的圖像生成1080的圖像呢?放大之后會不會存在間隙,這個間隙是否會導(dǎo)致圖像“發(fā)虛”呢? ? 做法如下: ? 先將目標(biāo)圖像的像素位置映射到原圖像的對應(yīng)位置上,然后把通過插值計(jì)算得到的原圖像對應(yīng)位置的像素值作為目標(biāo)圖像相應(yīng)位置的像素值。 ? 這樣操作之后是不是就沒有間隙了。這就是插值算法要干的事了。1080P目標(biāo)圖像中的(0,0)位置就映射到720P原圖像的(0,0)位置,取原圖像(0,0)位置的像素值作為目標(biāo)圖像(0,0)位置的像素值。目標(biāo)圖像的(1,1)位置就映射到原圖像中的(0.67,0.67)位置。最后,通過原圖像已有像素插值得到(0.67,0.67)位置的像素值,并將該像素值作為目標(biāo)圖像(1,1)位置的像素值。下圖中,顯示了720p放大到1080p的圖示和720p縮小到360p的圖示。 ?
? 通過這個演示,可以總結(jié)一下圖像縮放的一般過程: ?
放大過程對于1080P目標(biāo)圖像中的每一個像素點(diǎn)(x,y),我們只需要將它映射到 720P原圖像的(x / 1.5,y / 1.5)位置,通過原圖像已有的像素值插值得到(x / 1.5,y / 1.5)的像素值就可以了。
縮小過程對于360P目標(biāo)圖像中的每一個像素點(diǎn)(x,y),我們只需要將它映射到720P原圖像的(x * 2,y * 2)位置,通過原圖像已有的像素值插值得到(x * 2,y * 2)的像素值就可以了。
然后二者的操作過程是,遍歷一下目標(biāo)圖像中的每一個像素點(diǎn)位置,都能找到他們在原圖像中的映射位置,并通過插值求出映射位置的像素值,這樣就可以得到目標(biāo)圖像了 ? 我們可以推測出,縮放的代碼邏輯是: ?
獲取目標(biāo)圖像的寬高;
然后獲取獲取像素,完成像素插值
將處理過的像素創(chuàng)建新的圖像
不用著急,后面可以驗(yàn)證該推測。 ? 縮放的通用表達(dá)式 ? 假設(shè)原圖像的分辨率是 w0 x h0,我們需要縮放到 w1 x h1。那我們只需要將目標(biāo)圖像中的像素位置(x,y)映射到原圖像的(x * w0 / w1,y * h0 / h1),再插值得到這個像素值就可以了,這個插值得到的像素值就是目標(biāo)圖像像素點(diǎn)(x,y)的像素值。注意,(x * w0 / w1,y * h0 / h1)絕大多數(shù)時候是小數(shù)。這就是圖像縮放算法原理的通用表達(dá)示圖如下: ?
? 目前比較流行的插值算法基本有三種。 ? ?
插值算法
最近鄰插值算法 ? 最近鄰插值算法是一種基本的圖像插值算法,它的基本思想是對于待插值像素的位置,選擇距離該位置最近的一個已知像素值作為插值結(jié)果。因此,該算法得名為“最近鄰插值”。 ? 最近鄰插值算法的具體實(shí)現(xiàn)過程如下: ?
確定待插值像素位置,假設(shè)其坐標(biāo)為(x,y)。
找到離(x,y)最近的已知像素,假設(shè)其坐標(biāo)為(x1,y1)。
將該已知像素的像素值賦值給待插值像素。
還是看圖像從720p放大到1080p,這個例子,我們上面說了縮放的原理就是映射像素,即將目標(biāo)圖像的像素映射到原圖像。 ? 1080p假設(shè)(2,2)的位置,最鄰近插值法的取值過程是: ? 映射到720P圖像,映射位置是(2 * 1280 / 1920,2 * 720 / 1080),也就是(1.33,1.33)位置,其周圍4個像素分別是(1,1)、(1,2)、(2,1)和(2,2),很明顯(1,1)離(1.33l,1.33)位置最近,那我們?nèi)≡瓐D像(1,1)的像素值賦值給1080P圖像的(2,2)位置的像素點(diǎn)。 ?
? 左圖為放大過程,右圖為放大時取值圖示: ?
所以1080待插值的位置(2,2)
找到了離(2,2)最近的已知像素(1.33,1.33)
將(1.33,1.33)位置的像素賦值給(2,2),完成插值。
代碼實(shí)現(xiàn)
Java代碼開發(fā)
最近鄰插值算法java代碼_圖像最近鄰插值算法_異域拾荒人的博客-CSDN博客
https://blog.csdn.net/weixin_32024145/article/details/114813888
使用Android JNI開發(fā)
使用opencv 庫開發(fā),因?yàn)樾枰@取像素等操作,最核心的代碼為:??
????//?根據(jù)縮放比例計(jì)算輸出圖像的寬高 ????int?new_width?=?static_cast
于其只考慮最近鄰像素的值,而忽略了其他像素的信息,因此會導(dǎo)致圖像插值后的結(jié)果較為粗糙,缺乏細(xì)節(jié)和平滑性
直接使用離插值位置最近的整數(shù)位置的像素作為插值像素,這樣會導(dǎo)致相鄰兩個插值像素有很大的概率是相同的
應(yīng)用場景
快速預(yù)覽圖像或圖像縮小等場景。 ?
雙線性插值算法
? 雙線性插值算法是一種常用的圖像插值算法,它能夠通過對周圍4個已知像素進(jìn)行加權(quán)平均來計(jì)算待插值像素的像素值,從而獲得更為平滑和細(xì)膩的插值結(jié)果。聽著就比最近鄰插值算法復(fù)雜,但是效果肯定比它優(yōu)。 ? 雙線性插值算法的具體實(shí)現(xiàn)過程如下: ? 確定待插值像素位置,假設(shè)其坐標(biāo)為(x,y)。找到距離(x,y)最近的四個已知像素,假設(shè)它們的坐標(biāo)為(x1,y1)、(x2,y2)、(x3,y3)、(x4,y4)。計(jì)算待插值像素的像素值。假設(shè)待插值像素的像素值為f(x,y),則可以通過下面的公式計(jì)算: ? ?
? 其中,w和h分別表示待插值像素相對于(x1,y1)和(x2,y2)的水平距離和垂直距離,具體計(jì)算方式如下: ?
? 將計(jì)算得到的像素值賦給待插值像素。 ? 線性插值 ? 線性插值是一種數(shù)學(xué)方法,用于在兩個已知點(diǎn)之間估計(jì)未知點(diǎn)的值。它假定兩個已知點(diǎn)之間的函數(shù)是線性的,因此可以使用直線方程來計(jì)算未知點(diǎn)的值。 ? 線性插值認(rèn)為,這個需要插值得到的點(diǎn)跟這兩個已知點(diǎn)都有一定的關(guān)系,并且,待插值點(diǎn)與離它近的那個點(diǎn)更相似。因此,線性插值是一種以距離作為權(quán)重的插值方式,距離越近權(quán)重越大,距離越遠(yuǎn)權(quán)重越小。 ? 比如,如下圖所示,已知 (x1,y1)與(x2,y2)兩個點(diǎn),需求得x對應(yīng)的y值。 ?
?
? 它表示在兩個點(diǎn)(x1,y1)和(x2,y2)之間,對于給定的x值,對應(yīng)的y值可以通過這個公式計(jì)算得到。當(dāng)x=x1時,y=y1;當(dāng) x=x2 時,y=y2。當(dāng)x在x1和x2之間時,y 的值在y1和y2之間線性變化。 ? 雙線性插值 ? 雙線性插值本質(zhì)上就是在兩個方向上做線性插值。由于圖像是兩個方向的二維數(shù)據(jù),正好適合使用雙線性插值算法。雙線性插值其實(shí)就是三次線性插值的過程,我們先通過兩次線性插值得到兩個中間值,然后再通過對這兩個中間值進(jìn)行一次插值得到最終的結(jié)果。 ? 假設(shè)我們有一個矩形網(wǎng)格,其中四個頂點(diǎn)的坐標(biāo)分別為 (x1,y1),(x1,y2),(x2,y1) 和 (x2,y2),并且這些點(diǎn)的函數(shù)值分別為 f(x1,y1),f(x1,y2),f(x2,y1) 和 f(x2,y2)。我們想要計(jì)算點(diǎn) (x,y) 的函數(shù)值,其中 x 和 y 分別在 x1 和 x2 之間,在 y1 和 y2 之間。 ?
? 沿著x軸進(jìn)行線性插值,得到兩個臨時值。 ?
? 這個即圖中的n點(diǎn)。 ?
? 即圖的m點(diǎn),再沿著y軸線性插值。 ?
? ? 即圖中p以720P放大到1080P為例,那么1080P圖像中的目標(biāo)像素點(diǎn)(2,2)的雙線性插值過程是怎么樣的呢? ? 首先,將目標(biāo)像素點(diǎn)(2,2)映射到原圖像的(1.33,1.33)位置,對應(yīng)下面圖中的點(diǎn)p。找到(1.33,1.33)周圍的4個像素(1,1)、(2,1)、(1,2)和(2,2),分別對應(yīng)圖中的點(diǎn)a、b、c和d。 ?
? 根據(jù)上述的公式,我們可以計(jì)算出圖中的n、m 最后求出p 的像素。 ? 首先n點(diǎn): ?
? m點(diǎn): ?
? p點(diǎn): ?
? 值求得(1.33,1.33)的值之后,將其賦值給1080P目標(biāo)圖像的(2,2)位置的像素點(diǎn)就可以了。這就是雙線性插值的過程。 ? 優(yōu)缺點(diǎn) ? 優(yōu)點(diǎn):可以獲得相對較為平滑和細(xì)膩的插值結(jié)果,而且計(jì)算速度也比較快 缺點(diǎn): ?
只考慮了周圍4個像素的信息,因此在圖像放大的情況下,仍然會出現(xiàn)鋸齒狀的效果
在圖像存在大幅度變化或復(fù)雜紋理的情況下,雙線性插值算法可能會導(dǎo)致圖像失真或出現(xiàn)馬賽克效果
應(yīng)用場景 ? 圖像放大、縮小和旋轉(zhuǎn)等場景 ?
總結(jié)
此文章主在弄明白圖像縮放的原理,并完成了經(jīng)典算法插值算法的最近鄰插值算法,當(dāng)然,最近鄰插值算法的使用場景單一,也不是工作中常用的算法,后續(xù)會陸續(xù)完成雙三次插值算法。雙三次算法圖像質(zhì)量方面有明顯提升,特別是在縮小圖像時,雙三次插值算法可以更好地保留圖像的細(xì)節(jié)和紋理。
? 編輯:黃飛
?
評論