python 無損壓縮照片,支持批量壓縮,支持保留照片信息


由於雲盤空間有限,照片尺寸也是很大,所以寫個Python程序壓縮一下照片,騰出一些雲盤空間  

1、批量壓縮照片

 新建 photo_compress.py 代碼如下 

  1 # -*- coding: utf-8 -*-
  2 
  3 """腳本功能說明:使用 tinypng api,一鍵批量壓縮指定文件(夾)所有文件"""
  4 
  5 import os
  6 import sys
  7 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor  # 線程池,進程池
  8 import json
  9 import random
 10 import requests
 11 from you_get import common
 12 from shutil import copyfile
 13 
 14 
 15 def get_file_dir(file):
 16     """獲取文件目錄通用函數"""
 17     fullpath = os.path.abspath(os.path.realpath(file))
 18     return os.path.dirname(fullpath)
 19 
 20 
 21 def check_suffix(file_path):
 22     """檢查指定文件的后綴是否符合要求"""
 23     file_path_lower = file_path.lower()
 24     return (file_path_lower.endswith('.png')
 25             or file_path_lower.endswith('.jpg')
 26             or file_path_lower.endswith('.jpeg'))
 27 
 28 
 29 def download_tinypng(input_file, url, output_file):
 30     file_name = os.path.basename(input_file)
 31     arr = file_name.split('.')
 32     new_file_name = arr[len(arr) - 2] + '_compress'
 33     new_output_file = os.path.join(os.path.dirname(output_file), arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1])
 34     print(u'開始下載文件 :%s' % new_output_file)
 35     # print(os.path.splitext(os.path.basename(output_file))[0])
 36     sys.argv = ['you-get', '-o', os.path.dirname(
 37         output_file), '-O', new_file_name, url]
 38     common.main()
 39     old_size = os.path.getsize(input_file)
 40     new_size = os.path.getsize(new_output_file)
 41     print(u'文件保存地址:%s' % new_output_file)
 42     print(u'壓縮后文件大小:%d KB' % (new_size / 1024))
 43     print(u'壓縮比: %d%%' % ((old_size - new_size) * 100 / old_size))
 44 
 45 
 46 def compress_by_tinypng(input_file):
 47     if not check_suffix(input_file):
 48         print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
 49         return
 50 
 51     file_name = os.path.basename(input_file)
 52     arr = file_name.split('.')
 53     new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
 54     output_path = os.path.join(get_file_dir(input_file), 'compress_output')
 55     output_file = os.path.join(output_path, new_file_name)
 56     if not os.path.isdir(output_path):
 57         os.makedirs(output_path)
 58 
 59     if (os.path.exists(output_file)):
 60         print("已存在,跳過壓縮")
 61         return
 62 
 63     try:
 64         old_size = os.path.getsize(input_file)
 65         print(u'壓縮前文件名:%s文件大小:%d KB' % (input_file, old_size / 1024))
 66         if (old_size < 1024 * 1024):
 67             print("已跳過壓縮,並直接拷貝文件")
 68             try:
 69                 copyfile(input_file, output_file)
 70             except IOError as e:
 71                 print("Unable to copy file. %s" % e)
 72             return
 73         print("開始壓縮")
 74         shrink_image(input_file)
 75         print(u'文件壓縮成功:%s' % input_file)
 76         # download_thread_pool.submit(download_tinypng, source, input_file, output_file)
 77     except Exception as e:
 78         print(u'報錯了:%s' % e)
 79 
 80 
 81 def check_path(input_path):
 82     """如果輸入的是文件則直接壓縮,如果是文件夾則先遍歷"""
 83     if os.path.isfile(input_path):
 84         compress_by_tinypng(input_path)
 85     elif os.path.isdir(input_path):
 86         dirlist = os.walk(input_path)
 87         for root, dirs, files in dirlist:
 88             if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
 89                 i = 0
 90                 for filename in files:
 91                     i = i + 1
 92                     process_pool.submit(compress_by_tinypng, os.path.join(
 93                         root, filename))
 94                     # compress_by_tinypng(os.path.join(root, filename))
 95     else:
 96         print(u'目標文件(夾)不存在,請確認后重試。')
 97 
 98 
 99 def list_images(path):
100     images = None
101     try:
102         if path:
103             os.chdir(path)
104         full_path = os.getcwd()
105         files = os.listdir(full_path)
106         images = []
107         for file in files:
108             ext = os.path.splitext(file)[1].lower()
109             if ext in ('.jpg', '.jpeg', '.png'):
110                 images.append(os.path.join(full_path, file))
111     except:
112         pass
113     return images
114 
115 
116 def shrink_image(file_path):
117     print(u'源文件地址:%s' % file_path)
118     result = shrink(file_path)
119     if result:
120         output_path = generate_output_path(file_path)
121         url = result['output']['url']
122         print(u'下載地址:%s' % url)
123         download_tinypng(file_path, url, output_path)
124         # download_thread_pool.submit(download_tinypng, file_path, url,  output_path)
125         # response = requests.get(url)
126         # with open(output_path, 'wb') as file:
127         #     file.write(response.content)
128         # print(u'文件保存地址:%s' % output_path)
129         # print('%s %d=>%d(%f)' % (
130         #     result['input']['type'],
131         #     result['input']['size'],
132         #     result['output']['size'],
133         #     result['output']['ratio']
134         #     ))
135     else:
136         print('壓縮失敗')
137 
138 
139 def shrink(file_path):
140     url = 'https://tinypng.com/web/shrink'
141     headers = {
142         'Cache-Control': 'no-cache',
143         'Content-Type': 'application/x-www-form-urlencoded',
144         'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36 Edg/85.0.564.44',
145         'X-Forwarded-For': get_random_ip()
146     }
147     result = None
148     try:
149         file = open(file_path, 'rb')
150         response = requests.post(url, headers=headers, data=file)
151         result = json.loads(response.text)
152     except Exception as e:
153         print(u'報錯了:%s' % e)
154         if file:
155             file.close()
156     if result and result['input'] and result['output']:
157         return result
158     else:
159         return None
160 
161 
162 def generate_output_path(file_path):
163     parent_path = os.path.abspath(os.path.dirname(file_path))
164     output_path = os.path.join(parent_path, 'compress_output')
165     if not os.path.isdir(output_path):
166         os.mkdir(output_path)
167     return os.path.join(output_path, os.path.basename(file_path))
168 
169 
170 def get_random_ip():
171     ip = []
172     for i in range(4):
173         ip.append(str(random.randint(0 if i in (2, 3) else 1, 254)))
174     return '.'.join(ip)
175 
176 
177 if __name__ == '__main__':
178     thread_pool = ThreadPoolExecutor(5)  # 定義5個線程執行此任務
179     download_thread_pool = ThreadPoolExecutor(10)  # 定義5個線程執行此任務
180     process_pool = ProcessPoolExecutor(8)  # 定義5個進程
181     len_param = len(sys.argv)
182     if len_param != 2 and len_param != 3:
183         print('請使用: %s [filepath]' % os.path.basename(sys.argv[0]))
184     else:
185         check_path(sys.argv[1])
186         input("Press <enter> 請耐心等待\n")

 

 

 執行python .\photo_compress.py F:\\test

 生成compress_output文件夾,里面就是壓縮的文件,但此時的照片沒有,拍攝時的時間、位置的信息,所以下面要復制文件信息

 若要壓縮的文件不全,可以再執行一次壓縮(會自動過濾已壓縮的照片)

2、批量拷貝照片信息

  使用pyexiv2進行文件信息拷貝

pip install pyexiv2 -i https://pypi.tuna.tsinghua.edu.cn/simple

  新建 copy_fileinfo.py 代碼如下

  1 # -*- coding: utf-8 -*-
  2 
  3 """腳本功能說明:使用 pyexiv2 api,一鍵批量拷貝指定文件(夾)所有文件信息"""
  4 
  5 import os
  6 import sys
  7 from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor  # 線程池,進程池
  8 from pyexiv2 import Image
  9 
 10 
 11 def get_file_dir(file):
 12     """獲取文件目錄通用函數"""
 13     fullpath = os.path.abspath(os.path.realpath(file))
 14     return os.path.dirname(fullpath)
 15 
 16 
 17 def check_suffix(file_path):
 18     """檢查指定文件的后綴是否符合要求"""
 19     file_path_lower = file_path.lower()
 20     return (file_path_lower.endswith('.png')
 21             or file_path_lower.endswith('.jpg')
 22             or file_path_lower.endswith('.jpeg'))
 23 
 24 
 25 def copyinfo_by_pyexiv2(input_file):
 26     file_name = os.path.basename(input_file)
 27     arr = file_name.split('.')
 28     new_file_name = arr[len(arr) - 2] + '_compress.' + arr[len(arr) - 1]
 29     output_path = os.path.join(get_file_dir(input_file), 'compress_output')
 30     output_file = os.path.join(output_path, new_file_name)
 31     if not (check_suffix(input_file) or check_suffix(output_file)):
 32         print(u'只支持png\\jpg\\jepg格式文件:' + input_file)
 33         return
 34     if not (os.path.exists(output_file)):
 35         print(u'文件不存在:' + output_file)
 36         return
 37     old_size = os.path.getsize(input_file)
 38     if (old_size < 1024 * 1024):
 39         print(u"已跳過拷貝文件信息:", input_file)
 40         return
 41 
 42     # if not os.path.isdir(output_path):
 43     #     os.makedirs(output_path)
 44     try:
 45         i = Image(input_file)    # 源圖片路徑
 46     except Exception:
 47         i = Image(input_file, "GB18030")
 48 
 49     try:
 50         _exif_info = i.read_exif()
 51     except Exception:
 52         _exif_info = i.read_exif("GB18030")
 53 
 54     # print(_exif_info)
 55     # _iptc_info = i.read_iptc()
 56     # print(_iptc_info)
 57     # _xmp_info = i.read_xmp()
 58     # print(_xmp_info)
 59     i.close()
 60 
 61     try:
 62         i2 = Image(output_file)  # 拷貝信息圖片路徑
 63     except Exception:
 64         i2 = Image(output_file, "GB18030")
 65 
 66     try:
 67         _exif_info2 = i2.read_exif()
 68     except Exception:
 69         _exif_info2 = i2.read_exif("GB18030")
 70 
 71     # 方向不拷貝,防止圖片旋轉
 72     for item in _exif_info:
 73         if("Exif.Image.Orientation" != item):
 74             if (_exif_info2.get(item) != _exif_info.get(item)):
 75                 try:
 76                     i2.modify_exif({item: _exif_info[item]})
 77                 except Exception as e:
 78                     print(e)
 79                     try:
 80                         i2.modify_exif({item: _exif_info[item]}, "GB18030")
 81                     except Exception as e:
 82                         print(e)
 83 
 84     i2.close()
 85 
 86     print(u"拷貝信息完成:" + input_file)
 87 
 88 
 89 def check_path(input_path):
 90     """如果輸入的是文件則直接壓縮,如果是文件夾則先遍歷"""
 91     if os.path.isfile(input_path):
 92         copyinfo_by_pyexiv2(input_path)
 93     elif os.path.isdir(input_path):
 94         dirlist = os.walk(input_path)
 95         for root, dirs, files in dirlist:
 96             if (not (root.endswith("\\compress_output") or root.endswith("/compress_output"))):
 97                 i = 0
 98                 for filename in files:
 99                     i = i + 1
100                     process_pool.submit(copyinfo_by_pyexiv2, os.path.join(
101                         root, filename))
102     else:
103         print(u'目標文件(夾)不存在,請確認后重試。')
104 
105 
106 if __name__ == '__main__':
107     # thread_pool = ThreadPoolExecutor(10)  # 定義5個線程執行此任務
108     process_pool = ProcessPoolExecutor(8)  # 定義5個進程
109     len_param = len(sys.argv)
110     if len_param != 2:
111         print('請使用: %s [filepath]' % os.path.basename(sys.argv[0]))
112     else:
113         check_path(sys.argv[1])
114         input("Press <enter> 請耐心等待\n")

 

 執行python .\copy_fileinfo.py F:\\test

 大功告成!圖片壓縮完畢,信息還沒有丟失


免責聲明!

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



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