C#照片批量壓縮小工具


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

軟件界面如下:

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

 

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

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

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

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

            listView1.Items.Clear();
            imageList1.Images.Clear();

            DirectoryInfo TheFolder = new DirectoryInfo(folderBrowserDialog1.SelectedPath);//文件路徑
            List<string> ImgNames = new List<string>();
            string allowImg = ".jpg.jpeg.png.bmp";
            FileInfo[] Files = TheFolder.GetFiles();
            for (int i = 0; i < Files.Length; i++)//遍歷文件夾
            {
                if (Files[i].Length > 0 &&allowImg.IndexOf(Files[i].Extension.ToLower())>-1)//或者jpg,png 文件大小要大於0且是圖片文件
                {
                    Image image = Image.FromFile(Files[i].DirectoryName + "\\" + Files[i].Name);    //獲取文件                 
                    ImgNames.Add(Files[i].Name);//添加文件名
                    imageList1.Images.Add(image);//添加圖片
                }
            }
            //初始化設置
            this.listView1.View = View.LargeIcon;

            this.listView1.LargeImageList = this.imageList1;

            //開始綁定
            this.listView1.BeginUpdate();

            for (int i = 0; i < ImgNames.Count; i++)
            {
                ListViewItem lvi = new ListViewItem();

                lvi.ImageIndex = i;

                lvi.Text = ImgNames[i];

                this.listView1.Items.Add(lvi);
            }

            this.listView1.EndUpdate();

  解決辦法是用微軟提供的Windows API Code Pack 1.0.1庫,通過該庫可以直接使用到win7/vista/win8系統的一些特性功能,如資源管理器、桌面、任務欄等等。詳細介紹見官方主頁 

本程序使用WindowsApiCode完成對文件夾下的圖片迅速建縮略圖的代碼如下:

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

            //設置圖片展示控件屬性
            explorerBrowser1.ContentOptions.ViewMode = ExplorerBrowserViewMode.List;
            explorerBrowser1.NavigationOptions.PaneVisibility.Navigation = PaneVisibilityState.Hide;
            explorerBrowser1.NavigationOptions.PaneVisibility.CommandsView = PaneVisibilityState.Hide;
            explorerBrowser1.NavigationOptions.PaneVisibility.CommandsOrganize = PaneVisibilityState.Hide;
            explorerBrowser1.NavigationOptions.PaneVisibility.Commands = PaneVisibilityState.Hide;
            explorerBrowser1.SelectionChanged += new EventHandler(explorerBrowser1_SelectionChanged);

完成打開文件夾並顯示圖片縮略圖的代碼非常簡單:

       //打開圖片文件夾
        private void btnOpenDir_Click(object sender, EventArgs e)
        {
            // 創建打開文件夾對話框
            CommonOpenFileDialog cfd = new CommonOpenFileDialog();

            // 設置對話框屬性
            cfd.IsFolderPicker = true;
            cfd.AllowNonFileSystemItems = true;

            // 彈出對話框並返回用戶的選擇
            CommonFileDialogResult result = cfd.ShowDialog();

            //如果用戶確定
            if (result == CommonFileDialogResult.Ok)
            {
                // 獲取選擇對象的ShellObject形式
                ShellObject resultItem = cfd.FileAsShellObject;
                //用explorerBrowser控件顯示圖片列表
                explorerBrowser1.Navigate(resultItem);
            }
        }

采用這種方法打開圖片縮略圖列表時間可以忽略不計。

 

2.好看的圖片界面庫

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

本工具的界面我采用的

官方主頁為http://www.componentfactory.com/

 

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

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

.netFrameWork4.0里面提供了Parallel系列、Task系列來支持並行運算,讓並行計算變得如此簡單(為什么不跟着微軟走呢,后悔了吧 ^_^)。

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

本程序中並行進行圖片壓縮的代碼如下:

ParallelOptions po = new ParallelOptions();
            po.MaxDegreeOfParallelism = 15; //最多並發50個任務
            //並行進行圖片壓縮
            System.Threading.ThreadPool.QueueUserWorkItem(w=>{
                Parallel.ForEach(imgtoComp, po, (o) =>
                {
                    System.Drawing.Image sourceImg = System.Drawing.Image.FromFile(o.ParsingName);
                    int iWidth = 0;
                    int iHeight = 0;
                    if (rbtper.Checked)
                    {
                        int per = int.Parse(txtper.Text);
                        iWidth = sourceImg.Width * per / 100;
                        iHeight = sourceImg.Height * per / 100;
                    }

                    if (rbtheight.Checked)//最大高度
                    {
                        iHeight = int.Parse(txtheight.Text);
                        iWidth = iHeight * sourceImg.Width / sourceImg.Height;
                    }

                    if (rbtwidth.Checked)//最大寬度
                    {
                        iWidth = int.Parse(txtwidth.Text);
                        iHeight = iWidth * sourceImg.Height / sourceImg.Width;
                    }

                    System.Drawing.Image ThumbImg = ImgCompress.GetImageThumb(sourceImg, iWidth, iHeight);

                    if (rbtpng.Checked) ThumbImg.Save(FileSavePath + Path.GetFileNameWithoutExtension(o.Name) + ".png", System.Drawing.Imaging.ImageFormat.Png);
                    if (rbtgif.Checked) ThumbImg.Save(FileSavePath + Path.GetFileNameWithoutExtension(o.Name) + ".gif", System.Drawing.Imaging.ImageFormat.Gif);
                    if (rbtjpg.Checked) ThumbImg.Save(FileSavePath + Path.GetFileNameWithoutExtension(o.Name) + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

                    sourceImg.Dispose();
                    ThumbImg.Dispose();

                    Interlocked.Increment(ref ifinish);//ifinish++
                    
                    this.Invoke(this.mysetFinish, new Object[] { ifinish }); //刷新進度條等
                    
                });
            }, null);

  

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

並發數量通過ParallelOptions參數的MaxDegreeOfParallelism來設置,這里必須設置,否則幾百張5M的圖片同時跑,立馬內存就占滿了。

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

 

下載本程序  Demo

程序源碼 CODE

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM