像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。完成需求,倒是沒問題,但總覺得是不是應該有更合適的方案呢,如果有知道的朋友,歡迎提供知了一起探討!