當前位置:
首頁 > 最新 > png的故事:隔行掃描演算法

png的故事:隔行掃描演算法

來源:AlloyTeam

www.alloyteam.com/2017/06/the-story-of-png-deinterlacing-algorithm/

前言

前文已經講解過如何解析一張png圖片,然而對於掃描演算法里只是說明了逐行掃描的方式。其實png還支持一種隔行掃描技術,即Adam7隔行掃描演算法。

優劣

使用隔行掃描有什麼好處呢?如果大家有去仔細觀察的話,會發現網路上有一些png圖在載入時可以做到先顯示出比較模糊的圖片,然後逐漸越來越清晰,最後顯示出完整的圖片,類似如下效果:

GIF/56K

這就是隔行掃描能帶來的效果。隔行掃描一共會進行1到7次掃描,每一次都是跳著部分像素點進行掃描的,先掃描到像素點可以先渲染,每多一次掃描,圖片就會更清晰,到最後一次掃描時就會掃描完所有像素點,進而渲染出完整的圖片。

當然,也因為要進行跳像素掃描,整張圖片會存儲更多額外數據而導致圖片大小會稍微變大,具體增加了什麼額外數據下文會進行講解。

生成

要導出一張基於Adam7隔行掃描的png圖片是非常簡單,我們可以藉助Adobe的神器——PhotoShop(以下簡稱ps)。我們把一張普通的圖片拖入到ps中,然後依次點選【文件】-【存儲為Web所用的格式】,在彈出的框里選擇存儲為PNG-24,然後勾選交錯,最後點擊存儲即可。

這裡的交錯就是只將掃描演算法設為Adam7隔行掃描,如果不勾選交錯,則是普通逐行掃描的png圖片。

原理

Adam7隔行掃描演算法的原理並不難,本質上是將一張png圖片拆分成多張png小圖,然後對這幾張png小圖進行普通的逐行掃描解析,最後將解析出來的像素數據按照一定的規則進行歸位即可。

分析

在解壓縮完圖像數據後就要馬上進行拆圖。拆圖並不難,就是將原本存儲圖像數據的Buffer數組拆分成多個Buffer數組而已。關鍵的問題是怎麼拆,這時我們先祭上wiki上這張圖:

GIF/11K

上面這張圖就說明了每次掃描需要掃描到的像素,正常來說一張基於Adam7隔行掃描的png圖片是要經歷7次掃描的,不過有些比較小的圖片的實際掃描次數不到7次,這是因為有些掃描因為沒有實際像素點而落空的原因,所以下面的講解還是以標準的7次掃描來講解,本質上此演算法的代碼寫出來後,是能兼容任何大小的png圖片的,因為演算法本身和圖片大小無關。

7次掃描,其實就回答了上面拆圖的問題:要拆成7張小圖。每張小圖就包含了每次掃描時要歸位的像素點。

以第一次掃描為例:第一次掃描的規則是從左上角(我們設定此坐標為(0,0))開始,那麼它掃描到的下一個點是同一行上一個點往右偏移8個像素,即(8,0)。以此類推,再下一個點就是(16,0)、(24,0)等。噹噹前行所有符合規則的點都掃描完時則跳到下一個掃描行的起點,即(8,0),也就是說第一次掃描的掃描行也是以8個像素為偏移單位的。直到所有掃描行都已經掃描完成,我們就可以認為這次掃描已經結束,可以考慮進入第二次掃描。

我們以一張10*10大小的png圖片來舉例,下面每個數字代表一個像素點,數字的值代表這個點在第幾次掃描時被掃描到:

按照規則,在第一次掃描時我們會掃描到4個像素點,我們把這4個像素點單獨抽離出來合在一起,就是我們要拆的第一張小圖:

(1)6462646(1)6

(1)6462646(1)6

也就是說,我們的第一張小圖就是2*2大小的png圖片。後面的小圖大小以此類推,這樣我們就能得知拆圖的依據了。

拆圖

上面有提到,拆圖本質上就是把存放圖片數據的Buffer數組進行切分,在nodejs里的Buffer對象有個很好用的方法——slice,它的用法和數組的同名方法一樣。

直接用上面的例子,我們的第一張小圖是2*2點png圖片,在假設我們一個像素點所佔的位元組數是3個,那麼我們要切出來的第一個Buffer子數組的長度就是2*(2*3+1)。也許就有人好奇了,為什麼是乘以2*3+1而不是直接乘以2*3呢?之前我們提到過,拆成小圖後要對小圖進行普通的逐行掃描解析,這樣解析的話每一行的第一個位元組實際存放的不是圖像數據,而是過濾類型,因此每一行所佔用的位元組需要在2*3的基礎上加1。

像素歸位

其他的小圖拆分的方法是一樣,在最後一次掃描完畢後,我們就會拿到7張小圖。然後我們按照上面的規則對這些小圖的像素進行歸位,也就是填回去的意思。下面簡單演示下歸位的流程:

(1)()()()()()()()(1)()

()()()()()()()()()()

()()()()()()()()()()

11()()()()()()()()()()

11==>()()()()()()()()()()

()()()()()()()()()()

()()()()()()()()()()

()()()()()()()()()()

(1)()()()()()()()(1)()

()()()()()()()()()()

待到7張小圖的像素全部都歸位後,最後我們就能拿到一張完整的png圖片了。

代碼

整個流程的代碼如下:

letwidth;// 完整圖像寬度,解析IHDR數據塊可得

letheight;// 完整圖像高度,解析IHDR數據塊可得

letcolors;// 通道數,解析IHDR數據塊可得

letbitDepth;// 圖像深度,解析IHDR數據塊可得

letdata;// 完整圖像數據

letbytesPerPixel=Math.max(1,colors*bitDepth/8);// 每像素位元組數

letpixelsBuffer=Buffer.alloc(bytesPerPixel*width*height,0xFF);// 用來存放最後解析出來的圖像數據

// 7次掃描的規則

letstartX=[,,4,,2,,1];

letincX=[8,8,8,4,4,2,2];

letstartY=[,4,,2,,1,];

letincY=[8,8,4,4,2,2,1];

letoffset=;// 記錄小圖開始位置

// 7次掃描

for(leti=;i

// 子圖像信息

letsubWidth=Math.ceil((width-startY[i])/incY[i],10);// 小圖寬度

letsubHeight=Math.ceil((height-startX[i])/incX[i],10);// 小圖高度

letsubBytesPerRow=bytesPerPixel*subWidth;// 小圖每行位元組數

letoffsetEnd=offset+(subBytesPerRow+1)*subHeight;// 小圖結束位置

letsubData=data.slice(offset,offsetEnd);// 小圖像素數據

// 對小圖進行普通的逐行掃描

letsubPixelsBuffer=this.interlaceNone(subData,subWidth,subHeight,bytesPerPixel,subBytesPerRow);

letsubOffset=;

// 像素歸位

for(letx=startX[i];x

for(lety=startY[i];y

// 逐個像素拷貝回原本所在的位置

for(letz=;z

pixelsBuffer[(x*width+y)*bytesPerPixel+z]=subPixelsBuffer[subOffset++]&0xFF;

}

}

}

offset=offsetEnd;// 置為下一張小圖的開始位置

}

returnpixelsBuffer;

尾聲

整個Adam7隔行掃描的流程大概就是這樣:

覺得本文對你有幫助?請分享給更多人

關注「前端大全」,提升前端技能

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端大全 的精彩文章:

測試你的前端代碼–part2
Firefox 54 為何默認開啟四進程?Mozilla:不犧牲內存佔用
這 3個JS 性能基礎,讓 Bluebird 更快速
怎麼找工作,怎麼找到工作,怎麼找到滿意工作?

TAG:前端大全 |

您可能感興趣

大師們是如何讓EXCEL輕鬆實現隔行換色的?
隔行如隔山?手機處理器和PC處理器的區別在哪兒!
寫尺牘遇到這7種情況就要隔行!
莫言書法:隔行如隔山,在書法圈,莫言連小弟的名分都混不到吧
他不僅是外科醫生,還寫得一手好書法,醫生和書家隔行不隔理!
隔行如隔山?不存在的
隔行不隔山,綠地、景瑞如何玩轉電競產業?——產城研究之戰略觀察系列第21期
隔行如隔山
牙醫和攝影師都是暴利行業?網友透露「實情」,真是隔行如隔山
隔行如隔山:腫瘤科醫生常提到的專業辭彙,你能聽懂幾個?
隔行取利,角兒您這是跨界啊
趙薇、徐靜蕾、劉若英,誰才是隔行界中最成功的女導演?
飛機設計師去研發坦克會怎樣?隔行如隔山,根本研發不出靠譜東西
海爾2018AWE:5大領域差異點區隔行業!
隔行如隔山!博爾特試訓多特實為作秀公關
隔行互相敬仰!那些職業球星也有「迷弟」支持
蔡康永轉拍電影票房大敗,林志玲加持也沒招,網友:隔行如隔山