前言
圖片壓縮應用很廣泛,如生成縮略圖等。前期我在進行圖片處理的過程中碰到了一個問題,就是如何將圖片壓縮到指定尺寸,此處尺寸指的是生成圖片文件的大小。
我使用 opencv 進行圖片處理,於是想着直接使用 opencv 進行圖片壓縮處理, opencv 本身包含了壓縮到指定像素大小的方法,奈何尋找了很多方法均不能壓縮到指定文件尺寸,於是自己在思考后寫出了此方法。本文使用python語言。
一、 opencv 常規使用
opencv 無需多言,做過圖片處理的人應該都知道此類庫,下面我介紹一些常用方法。
1.1 安裝 opencv
首先安裝 python ,建議 python3 ,然后執行
:
pip install opencv-python
1.2 讀取圖片
首先引入 opencv 包:
import cv2 as cv
而后讀取圖片:
image = cv.imread(path)
其中 path 為圖片路徑, image 為圖片數據,是一個 numpy.ndarray 對象,其實就是一個多維數組。目前 opencv 支持幾乎所有格式的圖片(參考 http://blog.csdn.net/mars_xiaolei/article/details/78890971)。
1.3 保存圖片
代碼:
cv.imwrite(path, image)
其中 path 為保存的文件路徑, image 為讀取或者處理過的圖片數據, opencv 根據保存文件的后綴名來寫不同格式的圖片數據,所以后綴名一定要寫正確。
二、圖片壓縮
2.1 常規壓縮
opencv 支持常規壓縮,可以將圖片壓縮到指定的像素尺寸或者按比例縮放。
- 壓縮到指定的像素尺寸:
new_image = cv.resize(image, size)
其中 size 是一個二維元組,表示壓縮后圖片的寬高。
- 按比例縮放:
new_image = cv.resize(image, None, fx, fy)
其中 fx , fy 表示圖片在寬和高方向的壓縮了比例。
2.2 壓縮到指定文檔大小
有了上面的基礎我們來分析一下如何實現壓縮到指定文檔大小。
首先我們要讀取原始文檔的大小,算出原始文檔大小和壓縮目標值的比例,由於我們要實現的是寬高等比例壓縮,於是將其開根號即表示在單邊的壓縮比例,調用 2.1 節中的按比例壓縮。理論上一次就能達到效果,但是由於圖片本身存在壓縮,所以可能一次無法達到預期,只要對壓縮后的圖片重復此步驟,直到達到預期即可。
2.2.1 讀取文檔尺寸
def get_doc_size(path):
try:
size = os.path.getsize(path)
return get_mb_size(size)
except Exception as err:
print(err)
def get_mb_size(bytes):
bytes = float(bytes)
mb = bytes / 1024 / 1024
return mb
get_doc_size 函數返回圖片的文檔大小,單位為 MB 。
2.2.2 刪除文件
def delete_file(path):
if file_exist(path):
os.remove(path)
else:
print('no such file:%s' % path)
def file_exist(path):
return os.path.exists(path)
由於我們需要刪除壓縮過程中產生的中間文件,所以需要調用 delete_file 方法刪除之。
2.2.3 壓縮
size = get_doc_size(path)
delete_file(resize_path)
while size > filesize:
rate = math.ceil((size / filesize) * 10) / 10 + 0.1
rate = math.sqrt(rate)
rate = 1.0 / rate
if file_exist(resize_path):
resize_rate(resize_path, resize_path, rate, rate)
else:
resize_rate(path, resize_path, rate, rate)
size = get_doc_size(resize_path)
其中 filesize 表示壓縮目標值, path 表示原始文件路徑, resize_path 表示壓縮后存放路徑, resize_rate 表示上述按比例壓縮方法,定義如下:
def resize_rate(path, resize_path, fx, fy):
image = read_image(path)
im_resize = cv.resize(image, None, fx=fx, fy=fy)
delete_file(resize_path)
save_image(resize_path, im_resize)
def save_image(path, image):
cv.imwrite(path, image)
def read_image(path):
return cv.imread(path)
當然此處為了效果更好,我做了一些優化。
首先在獲取壓縮比例的時候我做了下述操作:
rate = math.ceil((size / filesize) * 10) / 10 + 0.1
理論情況應當是直接返回 size / filesize 即可,但是在實際測試過程中為了加速收斂,我采用上述方式,將一個小數先乘以 10 對其向上取整,這樣就表示精度保留到原始數值小數后 1 位,即如果是 3.14 將得到 32 ,而后將此結果再除以 10 , 即得到 3.2 ,所以最終結果就是對小數后第二位進行向上進位,最后結果又加了 0.1 以更快速的收斂,當然你也可以去掉。
實際測試發現,一般重復執行兩次即可得到理想的壓縮效果,並且結果值與理想壓縮尺寸相差無幾。
三、結論
本文簡單介紹了如何使用 opencv 將圖片壓縮到指定文件尺寸,當然你也可以選擇其他文件處理類庫而不是 opencv ,這個完全可以根據用戶自己的興趣而來,並且也可以優化最終的循環算法,以達到更佳的效果,或者更快的收斂。