如何快速的合成各類樣式的圖片

在某Q和某信中都有咱們熟悉的公衆號和結構化消息,例如html

又或者這樣:linux

這些圖片都是固定的,每一個用戶看到的圖片都是同一張。第一張,實在沒有太多的點擊慾望!第二張就還算湊合吧,不過哪來每天這麼多福利圖!c++

若是想讓每一個用戶看到的消息有所不一樣,因人而異,咱們須要依賴終端作相應的開發。git

例如某某運動的消息:github

這種方式就比固定圖片好不少了,用戶的點擊慾望明顯增強。很是好!除了開發週期慢一點以外。web

呃,不妙,很快又要過閱兵節,而後是月餅節!老闆想要作更炫酷的消息,怎麼辦?立刻打電話給終端同窗:「喂!喂?喂!3天能給我修改好模版嗎?」。不過終端同窗不是神,作得出來也發佈不了。。。算法

那麼,咱們聰明的產品同窗就想到了「動態合成圖片」的高招,立刻找到咱們先後臺開發。咱們一碰面,又立刻敲定能夠搞起,問題就是怎麼搞起了。api

 

好啦,廢話說完,該進入正題了。瀏覽器

 

消息的樣式:服務器

設計同窗是絕不手軟啊,真當這裏是網頁同樣,各類設計各類字體各類效果各類進度條。

 

項目狀況:

  • 100萬活躍用戶
  • 2小時內推送200萬個消息

200萬個消息也就是200萬個圖片,2小時內推送完成,也就是每秒大概300張圖片。3ms一張圖片?開什麼玩笑?你覺得每一個服務器都有博爾特這麼快嗎!你要知道,這個圖片並不小(630*350),要拼圖,還要編碼爲JPG/PNG什麼的。

挑戰是存在的,需求是要作的,任務是要完成的。咱們開始研究合成圖的各類現成的方案(那些從零開始實現各類尖端算法的思路就算了),包括:

  • C++圖形庫;
  • 瀏覽器截圖;
  • Flash。

什麼?瀏覽器截圖?什麼?Flash?服務器生成圖片,跟瀏覽器和Flash什麼事?

別急,咱們慢慢說明。

 

C++圖形庫:

後臺同窗哪一個不是精通C++,因此咱們的後臺同窗就開始研究各類C++方案。列出來一大堆:Boost.GIL、CImg、CxImage、FreeImage、Magick++(ImageMagick)、GDCM、ITK、OpenCV、VIGRA、VTK。各類高級術語,嚇你一跳。

首先,嘗試的是專業的Boost.GIL,但發現api羞澀難懂。後又轉到街知巷聞的ImageMagick,不少重構同窗都是用這個庫作圖片壓縮。因而,後臺同窗浴血奮戰,拼出了第一個效果:

,看來離目標效果不遠了。雖然這給人感受win10和win95的感受,但要知道,win95升級四、5次就到win10了。

不過,悲劇的還不是這個醜,悲劇的是,合成一張jpg一共須要耗時300ms!

請容許我掐指一算,300ms一張,一秒3張,要達到一秒300張的目標,就須要100臺機器。嗯,大老闆這麼有錢,應該不會介意的。

好吧,開個玩笑,後臺同窗完全放棄了。

 

瀏覽器截圖:

爲何要想瀏覽器截圖?其實之前在項目中用過,只不過當時並無這麼高的速度要求。畢竟設計稿就很是適合用網頁實現,若是瀏覽器截圖的速度能達到要求,那麼作這個動態圖片的成本就很低了。

有不少linux命令行工具,能夠對網頁截圖,原理是啓動webkit渲染網頁,而後截圖。例如gnome-screenshot、wkhtmltoimage。

實際狀況是讓人沮喪的,截圖隨便須要1秒2秒的時間。

不過,這個也是能理解的,畢竟要啓動webkit,網頁要刷新,再截圖,能不慢嗎?

 

Flash:

筆者自己作Flash出身,因此對Flash生成圖片情有獨鍾,既然如此,何不拿Flash測試一下呢?

通過測試,咱們發現Flash不單能輕鬆的完美復現設計的效果,並且截圖效率很是高,最終也選擇了這個方案。

不過,要讓Flash運行在linux服務器上,卻是要下一番功夫。

研究的內容包括:

  • Flash player or Air?
  • Flash和C++的通訊?
  • 高效壓縮圖片?

 

#Flash player or Air?

player和air只是swf運行的兩種形式而已,對速度不會有影響。研究這個目的是嘗試實現原來的通訊架構,由於Air模式才能在flash側運行ServerSocket。若是Flash能運行ServerSocket,那麼Flash就稱爲服務提供者,C++須要合成圖的時候,只須要鏈接socket,傳輸參數,而後接收圖片便可。

不過,Adobe於2011年宣佈從air 2.7開始再也不支持linux版本,因此否決了Air,仍是繼續使用Flash player。另外,要讓flash正常運行起來,還須要安裝xvfb服務。

 

#Flash和C++的通訊?

爲了保證高效的通訊,避免每次截圖都重啓Flash player,咱們設計了這樣的通訊機制:

C++控制Flash的生命週期,按期重啓Flash。Flash啓動後,立刻連接C++提供的socket服務。鏈接成功後,C++給Flash分配任務,傳輸相應的用戶數據;Flash接收數據後,拉取用戶頭像、生成圖片並壓縮爲JPG,再以二進制形式在socket中回傳給C++。回傳完畢後,Flash保持socket鏈接,等待新的任務。

 

#高效壓縮圖片?

圖片動態拼接完成後,須要壓縮爲png或者jpg,又或者更多其餘格式。固然,在當前的軟件環境來看,jpg和png是惟二的選擇了。

咱們作了不少測試,包括:

  • as3core壓縮80%的jpg和無損png,也就是as3代碼作編碼運算
  • 改進版pngencoder:https://github.com/cameron314/PNGEncoder2
  • flascc(alchemy c++加速)壓縮80%的jpg(as3_jpeg_wrapper)
  • png8和有損png24,使用的是blooddy(https://github.com/kenkozheng/blooddy),其中也有flascc加速。

大體的狀況以下:

image

綜合文件大小和壓縮時間,咱們暫時選擇了flascc壓縮的jpg。

但清晰度方面略有欠缺,80%質量的jpg在呈現文字時,邊緣會略有模糊。不過這隻在大屏機器上有細微的感受。後續可能會考慮改成blooddy壓縮的有損png24,雖然文件大小和耗時都比現有方案增長1倍,但圖片要清晰一些。不得不讚賞一下blooddy的做者,俄羅斯人作軟件要麼就不作,要作就是很牛逼的。

有一個基礎數據還沒列出,就是Flash拼接生成畫面的時間。這個卻是快的驚人,不算加載圖片的時間,只須要8ms左右。

那麼最終,Flash生成一張圖的時間大概就是40ms。

 

最後,請再容許我掐指一算。默默的打開計算器。。。

40ms*2000000/1000 = 80000s = 22.2小時

 

那麼若是同時有10臺機器,就大概能夠在2小時內發送完成了。雖然100臺機器搞不到,10臺機器仍是有辦法的

固然,實際狀況還有圖片傳輸的耗時(實際上這裏更大,要傳輸到公衆號平臺),實際須要200ms一張圖片,不過這些都是異步的,咱們單機啓動25個Flash進程,整體運行平滑。

相關文章
相關標籤/搜索