python实现马赛克拼图

直接上代码!

代码如下:

#!/usr/local/bin/python3
#  --*-- coding:utf8 --*-- import getopt
import sys
import os import logging
from PIL import Image
from multiprocessing import Process, Queue, cpu_count TILE_SIZE = 30  # 素材图片大小
TILE_MATCH_RES = 10  #配置指数  ,值越大匹配度越高,执行时间越长
ENLARGEMENT = 4  # 生成的图片是原始图片的多少倍 TILE_BLOCK_SIZE = int(TILE_SIZE / max(min(TILE_MATCH_RES, TILE_SIZE), 1))
WORKER_COUNT = max(cpu_count() - 1, 1)
EOQ_VALUE = None
WARN_INFO = """ *缺少有效参数*
    参数:
        -i [--image]     : 原图片地址
        -t [--tiles_dir] : 素材目录地址
        -o [--outfile]   : 输出文件地址 【可选】
""" class TileProcessor:
    def __init__(self, tiles_directory):
        self.tiles_directory = tiles_directory     def __process_tile(self, tile_path):
        try:
            img = Image.open(tile_path)
            # tiles must be square, so get the largest square that fits inside the image
            w = img.size[0]
            h = img.size[1]
            min_dimension = min(w, h)
            w_crop = (w - min_dimension) / 2
            h_crop = (h - min_dimension) / 2
            img = img.crop((w_crop, h_crop, w - w_crop, h - h_crop))
            large_tile_img = img.resize((TILE_SIZE, TILE_SIZE), Image.ANTIALIAS)
            small_tile_img = img.resize((int(TILE_SIZE / TILE_BLOCK_SIZE), int(TILE_SIZE / TILE_BLOCK_SIZE)),
                                        Image.ANTIALIAS)
            return (large_tile_img.convert('RGB'), small_tile_img.convert('RGB'))
        except Exception as e:
            logging.warning(e)
            return (None, None)     def get_tiles(self):
        large_tiles = []
        small_tiles = []         logging.info('从 \'%s\' 获取图片素材...' % self.tiles_directory)         # search the tiles directory recursively
        for root, subFolders, files in os.walk(self.tiles_directory):
            for tile_name in files:
                tile_path = os.path.join(root, tile_name)
                large_tile, small_tile = self.__process_tile(tile_path)
                logging.debug(large_tile)
                logging.debug(small_tile)
                if large_tile:
                    large_tiles.append(large_tile)
                    small_tiles.append(small_tile)         logging.info('读取素材 %s 完成.' % len(large_tiles))         return (large_tiles, small_tiles) class TargetImage:
    def __init__(self, image_path):
        self.image_path = image_path     def get_data(self):
        logging.info('处理主图片...')
        img = Image.open(self.image_path)
        w = img.size[0] * ENLARGEMENT
        h = img.size[1] * ENLARGEMENT
        large_img = img.resize((w, h), Image.ANTIALIAS)
        w_diff = (w % TILE_SIZE) / 2
        h_diff = (h % TILE_SIZE) / 2         # if necesary, crop the image slightly so we use a whole number of tiles horizontally and vertically
        if w_diff or h_diff:
            large_img = large_img.crop((w_diff, h_diff, w - w_diff, h - h_diff))         small_img = large_img.resize((int(w / TILE_BLOCK_SIZE), int(h / TILE_BLOCK_SIZE)), Image.ANTIALIAS)         image_data = (large_img.convert('RGB'), small_img.convert('RGB'))         logging.info('主图片处理完成.')         return image_data class TileFitter:
    def __init__(self, tiles_data):
        self.tiles_data = tiles_data     def __get_tile_diff(self, t1, t2, bail_out_value):
        diff = 0
        for i in range(len(t1)):
            # diff += (abs(t1[i][0] - t2[i][0]) + abs(t1[i][1] - t2[i][1]) + abs(t1[i][2] - t2[i][2]))
            diff += ((t1[i][0] - t2[i][0]) ** 2 + (t1[i][1] - t2[i][1]) ** 2 + (t1[i][2] - t2[i][2]) ** 2)
            if diff > bail_out_value:
                # we know already that this isnt going to be the best fit, so no point continuing with this tile
                return diff
        return diff     def get_best_fit_tile(self, img_data):
        best_fit_tile_index = None
        min_diff = sys.maxsize
        tile_index = 0         # go through each tile in turn looking for the best match for the part of the image represented by 'img_data'
        for tile_data in self.tiles_data:
            diff = self.__get_tile_diff(img_data, tile_data, min_diff)
            # logging.info(diff)
            if diff < min_diff:
                min_diff = diff
                best_fit_tile_index = tile_index
            tile_index += 1         return best_fit_tile_index def fit_tiles(work_queue, result_queue, tiles_data):
    # this function gets run by the worker processes, one on each CPU core
    tile_fitter = TileFitter(tiles_data)     while True:
        try:
            img_data, img_coords = work_queue.get(True)
            if img_data == EOQ_VALUE:
                break
            tile_index = tile_fitter.get_best_fit_tile(img_data)
            result_queue.put((img_coords, tile_index))
        except KeyboardInterrupt:
            pass     # let the result handler know that this worker has finished everything
    result_queue.put((EOQ_VALUE, EOQ_VALUE)) class ProgressCounter:
    def __init__(self, total):
        self.total = total
        self.counter = 0     def update(self):
        self.counter += 1
        sys.stdout.write(
            "进度: %s%% %s" % ((100 * self.counter / self.total), "\r"))         # sys.stdout.write("Progress: %s%% %s" % (100 * self.counter / self.total, "\r"))     sys.stdout.flush() class MosaicImage:
    def __init__(self, original_img, outfile):
        self.image = Image.new(original_img.mode, original_img.size)
        self.x_tile_count = int(original_img.size[0] / TILE_SIZE)
        self.y_tile_count = int(original_img.size[1] / TILE_SIZE)
        self.total_tiles = self.x_tile_count * self.y_tile_count
        self.outfile = outfile     def add_tile(self, tile_data, coords):
        img = Image.new('RGB', (TILE_SIZE, TILE_SIZE))
        img.putdata(tile_data)
        self.image.paste(img, coords)     def save(self, path):
        self.image.save(path) def build_mosaic(result_queue, all_tile_data_large, original_img_large, outfile):
    mosaic = MosaicImage(original_img_large, outfile)     active_workers = WORKER_COUNT
    while True:
        try:
            img_coords, best_fit_tile_index = result_queue.get()             if img_coords == EOQ_VALUE:
                active_workers -= 1
                if not active_workers:
                    break
            else:
                tile_data = all_tile_data_large[best_fit_tile_index]
                mosaic.add_tile(tile_data, img_coords)         except KeyboardInterrupt:
            pass     mosaic.save(mosaic.outfile)
    logging.info('============ 生成成功 ============') def compose(original_img, tiles, outfile):
    logging.info('生成图片中,按下 Ctrl-C 中断...')
    original_img_large, original_img_small = original_img
    tiles_large, tiles_small = tiles     mosaic = MosaicImage(original_img_large, outfile)     all_tile_data_large = list(map(lambda tile: list(tile.getdata()), tiles_large))
    all_tile_data_small = list(map(lambda tile: list(tile.getdata()), tiles_small))     work_queue = Queue(WORKER_COUNT)
    result_queue = Queue()     try:
        # start the worker processes that will build the mosaic image
        Process(target=build_mosaic, args=(result_queue, all_tile_data_large, original_img_large, outfile)).start()         # start the worker processes that will perform the tile fitting
        for n in range(WORKER_COUNT):
            Process(target=fit_tiles, args=(work_queue, result_queue, all_tile_data_small)).start()         progress = ProgressCounter(mosaic.x_tile_count * mosaic.y_tile_count)
        for x in range(mosaic.x_tile_count):
            for y in range(mosaic.y_tile_count):
                large_box = (x * TILE_SIZE, y * TILE_SIZE, (x + 1) * TILE_SIZE, (y + 1) * TILE_SIZE)
                small_box = (
                    x * TILE_SIZE / TILE_BLOCK_SIZE, y * TILE_SIZE / TILE_BLOCK_SIZE,
                    (x + 1) * TILE_SIZE / TILE_BLOCK_SIZE,
                    (y + 1) * TILE_SIZE / TILE_BLOCK_SIZE)
                work_queue.put((list(original_img_small.crop(small_box).getdata()), large_box))
                progress.update()     except KeyboardInterrupt:
        logging.info('\nHalting, saving partial image please wait...')     finally:
        # put these special values onto the queue to let the workers know they can terminate
        for n in range(WORKER_COUNT):
            work_queue.put((EOQ_VALUE, EOQ_VALUE)) def mosaic(img_path, tiles_path, outfile):
    tiles_data = TileProcessor(tiles_path).get_tiles()
    image_data = TargetImage(img_path).get_data()
    compose(image_data, tiles_data, output) if __name__ == '__main__':
    logging.basicConfig(filename='mosaic.log',
                        format='%(asctime)s  %(filename)s : %(levelname)s  %(message)s',
                        level=logging.INFO)
    logging.getLogger().addHandler(logging.StreamHandler())     opts, args = getopt.gnu_getopt(sys.argv[1:], 'i:t:o:ts:tr:e:', ['image=', 'tiles_dir=', 'outfile=',""])     base_image = None
    tiles_dir = None
    output = None
    for k, v in opts:
        if k in ("-i", "--image"):
            base_image = v
        if k in ("-t", "--tiles_dir"):
            tiles_dir = v
        if k in ("-o", "--outfile"):
            output = v     # base_image = None
    # tiles_dir = None     for value in (base_image, tiles_dir):
        if value is None:
            logging.error(WARN_INFO)
            sys.exit()     if output is None:
        output = './mosaic.jpg'     mosaic(base_image, tiles_dir, output)

注!!!***

这里不是直接运行的!这里你要在终端使用!

**命令:python mosaic_v2.py -i "D:\image\pic.jpg" -t "D:\image"

程序原图:

效果图:

python实现马赛克拼图!的更多相关文章

  1. 利用python爬虫爬取图片并且制作马赛克拼图

    想在妹子生日送妹子一张用零食(或者食物类好看的图片)拼成的马赛克拼图,因此探索了一番= =. 首先需要一个软件来制作马赛克拼图,这里使用Foto-Mosaik-Edda(网上也有在线制作的网站,但是我 ...

  2. 【Python还能干嘛】爬取微信好友头像完成马赛克拼图(千图成像)~

    马赛克拼图 何谓马赛克拼图(千图成像),简单来说就是将若干小图片平凑成为一张大图,如下图路飞一样,如果放大看你会发现里面都是一些海贼王里面的图片. Our Tragets 爬取所有微信好友的头像

  3. python 游戏(滑动拼图Slide_Puzzle)

    1. 游戏功能和流程图 实现16宫格滑动拼图,实现3个按钮(重置用户操作,重新开始游戏,解密游戏),后续难度,额外添加重置一次的按钮,解密算法的植入,数字改变为图片植入 游戏流程图 2. 游戏配置 配 ...

  4. python 游戏(记忆拼图Memory_Puzzle)

    1. 游戏功能和流程图 实现功能:翻开两个一样的牌子就显示,全部翻开游戏结束,设置5种图形,7种颜色,游戏开始提示随机8个牌子 游戏流程图 2. 游戏配置 配置游戏目录 配置游戏(game_conf. ...

  5. 80个Python练手项目列表

    80个Python练手项目列表   我若将死,给孩子留遗言,只留一句话:Repetition is the mother of all learning重复是学习之母.他们将来长大,学知识,技巧.爱情 ...

  6. 转:如何在ArcMap下将栅格图象矢量化的基本步骤 (对影像的校准和配准、栅格图象矢量化)

    矢量对象是以矢量的形式,即用方向和大小来综合表示目标的形式描述的对象.例如画面上的一段直线,一个矩形,一个点,一个圆,一个填充的封闭区域--等等. 矢量图形文件就是由这些矢量对象组合而成的描述性文件. ...

  7. 马赛克是否无法逆转?Python简单消除,看片无忧!

    图片水印,轻松去除 前段时间玩过了全民K歌,不知道大家是否玩过,还是做得挺好的,就我这嗓子都能唱出张学友的味道,其中更是有消除噪声的功能,就是朋友们都在吃鸡大叫,我在旁边唱歌依然不受影响. 既然声音可 ...

  8. Python: PS 滤镜--马赛克

    本文利用 Python 实现PS 滤镜中的马赛克效果,具体的算法原理和效果可以参考之前的博客: http://blog.csdn.net/matrix_space/article/details/30 ...

  9. opencv马赛克python实现

    最近要实现opencv视频打马赛克,在网上找了一下基本是C++的实现,好在原理一样,下面给出python实现. 原理和注意点,我都写在注释里了 import cv2 ##马赛克 def do_mosa ...

随机推荐

  1. charles使用教程

    概述 Charles是目前最强大的http调试工具,在界面和功能上远胜于Fiddler,同时是全平台支持.是收费软件,可以试用15分钟,下面提供了破解方法.   安装破解 https://www.cn ...

  2. servlet项目找不到java文件(由于照抄代码造成的bug)

    其实这个bug非常简单,实际上是由于路径配置问题,也是由于我照抄代码造成的(好孩子不要学!) 情况是这样的:java文件没有问题但是启动项目的时候出现了bug: 然后当我把GetData.java和J ...

  3. dir(dict)|字典的创建-添加-删除-修改-判断存在-取值等相关操作

    dir(dict) ####字典操作:创建-添加-删除-修改-判断存在-取值 #(一)创建字典: {} .等号. zip(). [(),()] #1.创建空字典 dict0 = {} #2.等号创建 ...

  4. git for windows 2.1版本git bash下git log乱码修复

    git bash,输入以下命令git config --global i18n.logoutputencoding utf-8git config quotepath false关闭git bash, ...

  5. shell编程:字符串练习题string.sh

    string.sh脚本 #!/bin/bash # string="Bigdata process framework is Hadoop,Hadoop is an open source ...

  6. java性能调优03

    1.java中的四种引用类型(级别由高到低为:强引用,软引用,弱引用和虚引用) 1.1 强引用:默认创建的变量都是强引用,垃圾回收机制不会将其回收,当内存空 间不足,Java虚拟机宁愿抛出OutOfM ...

  7. Python之OS(系统操作)模块常用函数

    mkdir(path[, mode=0777]) makedirs(name,mode=511) rmdir(path) removedirs(path) listdir(path) getcwd() ...

  8. 嵌入式平台 RAM与ROM区分

    ROM(Read Only Memory)和RAM(Random Access Memory)指的都是半导体存储器,ROM在系统停止供电的时候仍然可以保持数据,而RAM通常是在掉电之后就丢失数据,典型 ...

  9. segment fault 定位 与 远程 gdb

    远程 GDB  首先 ,Target 为 ARM开发板 (IP =  192.168.1.200),HOST 为 Ubuntu 14.04 虚拟机 (IP = 192.168.1.4) 1. 下载  ...

  10. redis实现分布式锁需要考虑的因素以及可重入锁实现

    死锁 错误例子 解决方式  防止死锁 通过设置超时时间  不要使用setnx key   expire 20  不能保证原子性 如果setnx程序就挂了 没有执行expire就死锁了  reidis2 ...