1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| import numpy as np import matplotlib.pyplot as plt import PIL.Image import argparse
def load_img(path): img = PIL.Image.open(path) img = np.array(img).astype('float32') return img
def compress(path, rate): img = load_img(path) img = np.transpose(img, (2, 0, 1)) u, s, v = np.linalg.svd(img) if rate >= 1: return u, s, v m, n = img.size k = rate_to_k(m, n, rate) return truncate(u, s, v, k)
def truncate(u, s, v, k): u = u[..., :k] s = s[:, :k] v = v[:, :k] return u, s, v
def rate_to_k(m, n, rate): ''' 设原图像size为m*n 则占用空间为m*n*3 设保留k个奇异值 压缩后占用空间为(m+n+1)*k*3*4 压缩率为rate=(m+n+1)*k*4/(m*n) k = rate*m*n/((m+n+1)*4) ''' return int(rate*m*n/((m+n+1)*4))
def decompress(u, s, v): img = (u * s[:, np.newaxis]) @ v img = np.transpose(img, (1, 2, 0)) img = np.round(img.clip(0, 255)).astype('uint8') return img
def preview(path, rates, col=5): row = (len(rates) + col - 1) // col fig, axes = plt.subplots(row, col) for i in axes.flat: i.axis('off') u, s, v = compress(path, 1) m, n = PIL.Image.open(path).size for i, rate in enumerate(rates): k = rate_to_k(m, n, rate) img = decompress(*truncate(u, s, v, k)) ax = axes[i // col, i % col] ax.set_title(f'rate={rate}') ax.imshow(img) plt.show()
def save(path, u, s, v): np.savez_compressed(path, u=u, s=s, v=v)
def load_c(path): d = np.load(path) return d['u'], d['s'], d['v']
def main(): parser = argparse.ArgumentParser(description="SVD Image Compression") parser.add_argument('-c', '--compress', metavar='FILE', help='Compress an image') parser.add_argument('-o', '--output', metavar='FILE', help='Specify output file for compression') parser.add_argument('-r', '--rate', type=float, help='Compression rate')
parser.add_argument('-d', '--decompress', metavar='FILE', help='Decompress a compressed file')
parser.add_argument('-p', '--preview', metavar='FILE', help='Preview the compressed images') parser.add_argument('--rates', type=float, nargs='+', help='Specify compression rates for preview')
args = parser.parse_args()
if args.compress: u, s, v = compress(args.compress, args.rate or 0.8) output = args.output or args.compress if not output.endswith('.npz'): output += '.npz' save(output, u, s, v) print(f'Image compressed and saved to {output}')
elif args.decompress: u, s, v = load_c(args.decompress) output = args.output or args.decompress if output.endswith('.npz'): output = output[:-4] img = decompress(u, s, v) PIL.Image.fromarray(img).save(output) print(f'Image decompressed and saved to {output}')
elif args.preview: rates = args.rates or [ 1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1 ] preview(args.preview, rates)
else: parser.print_help()
if __name__ == '__main__': main()
|