【問題帖】壓縮圖片大小至指定Kb以下


  像PS,QQ影像等都有該功能,將圖片大小壓縮至指定kb以下。

  我也來山寨一把,到目前為止,控制圖片的大小,平時的解決方案通過分辨率和質量來控制的。

  假定最后壓縮的大小是100kb,那么在保證不大於100kb的前提下,圖片質量盡可能高。圖片質量越高,圖片占用大小就越大。但是大小與質量的關系,沒有一個固定的公式,如y= nx 之類的,而且我也試過將win7系統的圖片收藏夾的圖,每一張保存10次,從質量為10,遞增到100,發現只能得出之前的結論,圖片質量高,占用大小就大。

  既然這樣,那只能找到滿足1ookb大小的最合適的質量參數了。這里使用了二分法來查找。

        /// <summary>
        /// 壓縮圖片至n Kb以下
        /// </summary>
        /// <param name="img">圖片</param>
        /// <param name="format">圖片格式</param>
        /// <param name="targetLen">壓縮后大小</param>
        /// <param name="srcLen">原始大小</param>
        /// <returns>壓縮后的圖片內存流</returns>
        public static MemoryStream Zip(Image img, ImageFormat format, long targetLen, long srcLen = 0)
        {
            //設置允許大小偏差幅度 默認10kb
            const long nearlyLen = 10240;

            //返回內存流  如果參數中原圖大小沒有傳遞 則使用內存流讀取
            var ms = new MemoryStream();
            if (0 == srcLen)
            {
                img.Save(ms, format);
                srcLen = ms.Length;
            }

            //單位 由Kb轉為byte 若目標大小高於原圖大小,則滿足條件退出
            targetLen *= 1024;
            if (targetLen >= srcLen)
            {
                ms.SetLength(0);
                ms.Position = 0;
                img.Save(ms, format);
                return ms;
            }

            //獲取目標大小最低值
            var exitLen = targetLen - nearlyLen;

            //初始化質量壓縮參數 圖像 內存流等
            var quality = (long)Math.Floor(100.00 * targetLen / srcLen);
            var parms = new EncoderParameters(1);

            //獲取編碼器信息
            ImageCodecInfo formatInfo = null;
            var encoders = ImageCodecInfo.GetImageEncoders();
            foreach (ImageCodecInfo icf in encoders)
            {
                if (icf.FormatID == format.Guid)
                {
                    formatInfo = icf;
                    break;
                }
            }

            //使用二分法進行查找 最接近的質量參數
            long startQuality = quality;
            long endQuality = 100;
            quality = (startQuality + endQuality) / 2;

            while (true)
            {
                //設置質量
                parms.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);

                //清空內存流 然后保存圖片
                ms.SetLength(0);
                ms.Position = 0;
                img.Save(ms, formatInfo, parms);

                //若壓縮后大小低於目標大小,則滿足條件退出
                if (ms.Length >= exitLen && ms.Length <= targetLen)
                {
                    break;
                }
                else if (startQuality >= endQuality) //區間相等無需再次計算
                {
                    break;
                }
                else if (ms.Length < exitLen) //壓縮過小,起始質量右移
                {
                    startQuality = quality;
                }
                else //壓縮過大 終止質量左移
                {
                    endQuality = quality;
                }

                //重新設置質量參數 如果計算出來的質量沒有發生變化,則終止查找。這樣是為了避免重復計算情況{start:16,end:18} 和 {start:16,endQuality:17}
                var newQuality = (startQuality + endQuality) / 2;
                if (newQuality == quality)
                {
                    break;
                }
                quality = newQuality;

                Console.WriteLine("start:{0} end:{1} current:{2}", startQuality, endQuality, quality);
            }
            return ms;
        }
 

測試過程中發現,每張圖的處理時間差不多為1s。完成需求,倒是沒問題,但總覺得是不是應該有更合適的方案呢,如果有知道的朋友,歡迎提供知了一起探討!

測試代碼下載


免責聲明!

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



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