sklearn 实现极简VQ算法

VQ算法是一种图像压缩算法。本文用 sklearn 实现VQ算法。

代码简单的要哭

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
from sklearn.mixture import *
from sklearn.cluster import *

from PIL import Image

def vq(im, model=None):
    # vector quantization with a clustering model
    # im: an image read by pillow
    # model: a clustering model
    if model is None:
        model = KMeans(n_clusters=2)

    I = np.asarray(im, dtype=np.float64)
    X = I.reshape((im.size[0]*im.size[1], 3))
 
    model.fit(X)
    code = model.predict(X)  # encoding
    if hasattr(model, 'cluster_centers_'):
        Y = np.array([model.cluster_centers_[k] for k in code])   # decode with the codebook 'model.cluster_centers_'
    elif hasattr(model, 'means_'):
        Y = np.array([model.means_[k] for k in code])   # decode with the codebook 'model.means_'
    Y = Y.reshape((im.size[1], im.size[0])+(3,))
    return Image.fromarray(Y.astype('uint8', copy=False))

def test():
    # 实例
    model = BayesianGaussianMixture(n_components=2)
    im = Image.open('lenna.jpg')
    im = vq(im, model)
    im.save('lenna.vq2.jpg')

注:下图采用的是Kmeans
在这里插入图片描述

面向对象风格

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""VQ algorithm for image compression

Reference: https://www.sciencedirect.com/topics/engineering/vector-quantization
"""

import numpy as np
from sklearn.mixture import *
from sklearn.cluster import *

from PIL import Image

class ImageTransformer:
    pass

class VQTransformer(ImageTransformer):
    """VQ algorithm for image compression

    Example:
        model = KMeans(n_clusters=2)
        im = Image.open('cloth.jpg')
        t = VQTransformer(model=model)
        t.fit(im)
        im = t.transform(im)
        im.save('cloth.kmeans2.jpg')
    """

    def __init__(self, small_size=(1, 1), model=None):
        super().__init__()
        if model is None:
            model = KMeans(n_clusters=2)
        self.small_size = small_size
        self.model = model

    def fit(self, im, shape=None):
        X, shape = image2data(im, small_size=self.small_size, shape=shape)
        self.shape = shape
        self.model.fit(X)
        if hasattr(self.model, 'cluster_centers_'):
            self.codebook_ = self.model.cluster_centers_
        elif hasattr(self.model, 'means_'):
            self.codebook_ = self.model.means_
        return self

    def get_code(self, k):
        w, h = self.small_size
        return self.codebook_[k].reshape((h, w, 3))

    def _decode(self, code):
        # decode with the codebook
        return np.array([self.codebook_[k] for k in code])

    def encode(self, im, shape=None):
        X, shape = image2data(im, small_size=self.small_size, shape=shape)
        return self.model.predict(X), shape

    def decode(self, code, shape):
        Y = self._decode(code)
        if self.small_size == (1,1):
            Y = Y.reshape((im.size[1], im.size[0])+(3,))
            return Image.fromarray(Y.astype('uint8'))
        else:
            return data2image(Y, small_size=small_size, shape=shape)

    def transform(self, im):
        code, shape = self.encode(im)
        return self.decode(code, shape)

    def stat(self, im, shape=None):
        Y, shape = self.encode(im, shape=shape)
        import collections
        f = collections.Counter(Y)
        N = len(Y)
        p = {k: (v / N) for k, v in f.items()}
        return f, p


def image2data(im, small_size=(1,1), shape=None):
    """Transform an image to 2d array (number of pixels/wh X 3wh)
    where small_size = (w, h)
    
    Arguments:
        im {Image} -- an image
    
    Keyword Arguments:
        small_size {tuple} -- a rectangle domain of an image (default: (1, 1))
        shape {tuple} -- size of small domain (default: {None})
                        calculated based on the size of the image by default
    
    Returns:
        tuple of a block matrix and shape=(r, c)
    """
    w, h = small_size
    if shape is None:
        W, H = im.size
        c, cc = divmod(W, w)
        r, rr = divmod(H, h)
        shape = r, c
        im = im.resize((w*c, h*r))
    else:
        r, c = shape
        im = im.resize((0, 0, w*c, h*r))
    I = np.asarray(im, dtype=np.float64)
    shape = (r, c)
    block = []
    for i in range(r):
        for j in range(c):
            J = I[i*h:(i+1)*h, j*w:(j+1)*w]
            block.append(J.ravel())
    return np.array(block), shape

def data2image(X, small_size, shape):
    # inverse of image2data
    r, c = shape
    w, h = small_size
    X = X.reshape((r, c, w*h*3))
    X = np.block([[[X[i, j].reshape((h, w, 3))] for j in range(c)] for i in range(r)])
    return Image.fromarray(X.astype('uint8', copy=False))
    

def simplevq(im, model=None):
    # vector quantization with a clustering model
    if model is None:
        model = KMeans(n_clusters=2)

    X, r, c = image2data(im, small_size=(2,2))

    model.fit(X)
    code = model.predict(X)  # encoding
    if hasattr(model, 'cluster_centers_'):
        codebook = model.cluster_centers_
    elif hasattr(model, 'means_'):
        codebook = model.means_
    Y = np.array([model.codebook[k] for k in code]) 
    Y = Y.reshape((im.size[1], im.size[0])+(3,))
    return Image.fromarray(Y.astype('uint8', copy=False))

def vq(im, small_size=(2,2), model=None):
    # vector quantization with a clustering model
    if model is None:
        model = KMeans(n_clusters=2)

    X, shape = image2data(im, small_size=small_size)

    model.fit(X)
    code = model.predict(X)  # encoding
    if hasattr(model, 'cluster_centers_'):
        codebook = model.cluster_centers_
    elif hasattr(model, 'means_'):
        codebook = model.means_
    Y = np.array([model.codebook[k] for k in code]) 
    
    return data2image(Y, small_size=small_size, shape=shape)


if __name__ == '__main__':
    model = KMeans(n_clusters=2)
    im = Image.open('cloth.jpg')
    t = VQTransformer(model=model)
    t.fit(im)
    im = t.transform(im)
    im.save('cloth.kmeans2.jpg')
    print(t.stat(im))
    print(t.codebook_)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值