天天看點

C#照片批量壓縮小工具

原文:

做了一個照片批量壓縮工具,其實核心代碼幾分鐘就完成了,但整個小工具做下來還是花了一天的時間。中間遇到了大堆問題,并尋求最好的解決方案予以解決。現在就分享一下這個看似簡單的小工具所使用的技術。

軟體界面如下:

C#照片批量壓縮小工具

要做真實場景的測試,拿的都是單反照的大相片:圖檔尺寸3888*2592  圖檔大小5.37m:

C#照片批量壓縮小工具

其中遇到的問題與解決方案分享:

1.用listview顯示圖檔縮略圖非常慢的問題

這個問題是始料未及的,如果不做也可以,但是沒有縮略圖就有損軟體體驗,這是所有最求完美的程式員所不能容忍的,我當然也不例外。

最初的代碼如下:(此方法加載每張5m左右的圖檔需要200-500ms)

<code>listview1.items.clear();</code>

<code>imagelist1.images.clear();</code>

<code>directoryinfo thefolder =</code><code>new</code>

<code>directoryinfo(folderbrowserdialog1.selectedpath);</code><code>//檔案路徑</code>

<code>list&lt;</code><code>string</code><code>&gt; imgnames =</code><code>new</code>

<code>list&lt;</code><code>string</code><code>&gt;();</code>

<code>string</code>

<code>allowimg =</code><code>".jpg.jpeg.png.bmp"</code><code>;</code>

<code>fileinfo[] files = thefolder.getfiles();</code>

<code>for</code> <code>(</code><code>int</code>

<code>i = 0; i &lt; files.length; i++)</code><code>//周遊檔案夾</code>

<code>{</code>

<code>    </code><code>if</code>

<code>(files[i].length &gt; 0 &amp;&amp;allowimg.indexof(files[i].extension.tolower())&gt;-1)</code><code>//或者jpg,png 檔案大小要大于0且是圖檔檔案</code>

<code>    </code><code>{</code>

<code>        </code><code>image image = image.fromfile(files[i].directoryname +</code><code>"\\"</code>

<code>+ files[i].name);   </code><code>//擷取檔案                </code>

<code>        </code><code>imgnames.add(files[i].name);</code><code>//添加檔案名</code>

<code>        </code><code>imagelist1.images.add(image);</code><code>//添加圖檔</code>

<code>    </code><code>}</code>

<code>}</code>

<code>//初始化設定</code>

<code>this</code><code>.listview1.view = view.largeicon;</code>

<code>this</code><code>.listview1.largeimagelist =</code><code>this</code><code>.imagelist1;</code>

<code>//開始綁定</code>

<code>this</code><code>.listview1.beginupdate();</code>

<code>for</code>

<code>(</code><code>int</code>

<code>i = 0; i &lt; imgnames.count; i++)</code>

<code>    </code><code>listviewitem lvi =</code><code>new</code>

<code>listviewitem();</code>

<code>    </code><code>lvi.imageindex = i;</code>

<code>    </code><code>lvi.text = imgnames[i];</code>

<code>    </code><code>this</code><code>.listview1.items.add(lvi);</code>

<code>this</code><code>.listview1.endupdate();</code>

  解決辦法是用微軟提供的windows

api code pack

1.0.1庫,通過該庫可以直接使用到win7/vista/win8系統的一些特性功能,如資料總管、桌面、工作列等等。詳細介紹見 

本程式使用windowsapicode完成對檔案夾下的圖檔迅速建縮略圖的代碼如下:

先在界面上添加一個該庫提供的explorerbrowser控件,然後初始化該控件:

<code>//設定圖檔展示控件屬性</code>

<code>explorerbrowser1.contentoptions.viewmode = explorerbrowserviewmode.list;</code>

<code>explorerbrowser1.navigationoptions.panevisibility.navigation = panevisibilitystate.hide;</code>

<code>explorerbrowser1.navigationoptions.panevisibility.commandsview = panevisibilitystate.hide;</code>

<code>explorerbrowser1.navigationoptions.panevisibility.commandsorganize = panevisibilitystate.hide;</code>

<code>explorerbrowser1.navigationoptions.panevisibility.commands = panevisibilitystate.hide;</code>

<code>explorerbrowser1.selectionchanged +=</code><code>new</code>

<code>eventhandler(explorerbrowser1_selectionchanged);</code>

完成打開檔案夾并顯示圖檔縮略圖的代碼非常簡單:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<code>//打開圖檔檔案夾</code>

<code> </code><code>private</code>

<code>void</code> <code>btnopendir_click(</code><code>object</code>

<code>sender, eventargs e)</code>

<code> </code><code>{</code>

<code>     </code><code>// 建立打開檔案夾對話框</code>

<code>     </code><code>commonopenfiledialog cfd =</code><code>new</code>

<code>commonopenfiledialog();</code>

<code>     </code><code>// 設定對話框屬性</code>

<code>     </code><code>cfd.isfolderpicker =</code><code>true</code><code>;</code>

<code>     </code><code>cfd.allownonfilesystemitems =</code><code>true</code><code>;</code>

<code>     </code><code>// 彈出對話框并傳回使用者的選擇</code>

<code>     </code><code>commonfiledialogresult result = cfd.showdialog();</code>

<code>     </code><code>//如果使用者确定</code>

<code>     </code><code>if</code>

<code>(result == commonfiledialogresult.ok)</code>

<code>     </code><code>{</code>

<code>         </code><code>// 擷取選擇對象的shellobject形式</code>

<code>         </code><code>shellobject resultitem = cfd.fileasshellobject;</code>

<code>         </code><code>//用explorerbrowser控件顯示圖檔清單</code>

<code>         </code><code>explorerbrowser1.navigate(resultitem);</code>

<code>     </code><code>}</code>

<code> </code><code>}</code>

采用這種方法打開圖檔縮略圖清單時間可以忽略不計。

2.好看的圖檔界面庫

從前面的界面可以看出,本工具的界面并不醜,可以說還很精美,這也是花了心思的。

本工具的界面我采用的是

C#照片批量壓縮小工具

官方首頁為

3.充分利用多核并行計算,提高圖檔處理速度

處理批量任務當然要考慮速度,否則就失去了工具的意義了

.netframework4.0裡面提供了parallel系列、task系列來支援并行運算,讓并行計算變得如此簡單(為什麼不跟着微軟走呢,後悔了吧

^_^)。

并行指的是利用現在的cup多核,同時開啟多個任務。跟以往的并發計算不同的是,并發的多個線程其實并非真正同時在運作,他們隻是按照時間片,走走停停,邏輯上在同時進行,而并行則是在多個完全獨立的核上同時運作任務,是真正的同時在跑。

本程式中并行進行圖檔壓縮的代碼如下:

<code>paralleloptions po =</code><code>new</code>

<code>paralleloptions();</code>

<code>po.maxdegreeofparallelism = 15;</code><code>//最多并發15個任務</code>

<code>//并行進行圖檔壓縮</code>

<code>parallel.foreach(imgtocomp, po, (o) =&gt;</code>

<code>    </code><code>system.drawing.image sourceimg = system.drawing.image.fromfile(o.parsingname);</code>

<code>    </code><code>int</code>

<code>iwidth = 0;</code>

<code>iheight = 0;</code>

<code>(rbtper.checked)</code>

<code>        </code><code>int</code>

<code>per =</code><code>int</code><code>.parse(txtper.text);</code>

<code>        </code><code>iwidth = sourceimg.width * per / 100;</code>

<code>        </code><code>iheight = sourceimg.height * per / 100;</code>

<code>(rbtheight.checked)</code><code>//最大高度</code>

<code>        </code><code>iheight =</code><code>int</code><code>.parse(txtheight.text);</code>

<code>        </code><code>iwidth = iheight * sourceimg.width / sourceimg.height;</code>

<code>(rbtwidth.checked)</code><code>//最大寬度</code>

<code>        </code><code>iwidth =</code><code>int</code><code>.parse(txtwidth.text);</code>

<code>        </code><code>iheight = iwidth * sourceimg.height / sourceimg.width;</code>

<code>    </code><code>system.drawing.image thumbimg = imgcompress.getimagethumb(sourceimg, iwidth, iheight);</code>

<code>(rbtpng.checked) thumbimg.save(filesavepath+o.name,system.drawing.imaging.imageformat.png);</code>

<code>(rbtgif.checked) thumbimg.save(filesavepath + o.name, system.drawing.imaging.imageformat.gif);</code>

<code>(rbtjpg.checked) thumbimg.save(filesavepath + o.name, system.drawing.imaging.imageformat.jpeg);</code>

<code>    </code><code>sourceimg.dispose();</code>

<code>    </code><code>thumbimg.dispose();</code>

<code>    </code><code>ifinish++;</code>

<code>    </code><code>this</code><code>.invoke(</code><code>this</code><code>.mysetfinish,</code><code>new</code>

<code>object[] { ifinish });</code><code>//重新整理進度條等</code>

<code>});</code>

這裡主要強調一下并發任務數量的設定、以及資源的顯示釋放。

并發數量通過paralleloptions參數的maxdegreeofparallelism來設定,這裡必須設定,否則幾百張5m的圖檔同時跑,立馬記憶體就占滿了。

資源的顯式釋放:sourceimg.dispose();  thumbimg.dispose(); 

這點也非常重要,處理大圖檔是非常耗記憶體的,測試過程中就因為沒有顯式釋放記憶體,偷懶想着.net的自動垃圾回收機制會幫忙善後,結果跑到40多張圖檔的時候就記憶體不足了。顯式處理資源釋放後,壓縮圖檔的速度也因為空餘的記憶體比較多而變快了。

下載下傳本程式 

上一篇: 素數求和
下一篇: Fibonacci數