Python学习笔记17:玩转千图成像

千图成像,顾名思义,就是用上千张上万张图片合成一张超大的图。

Python学习笔记17:玩转千图成像_第1张图片

今天,我用python来玩玩~

准备素材图 pic.jpg 。大小随意,不建议太大,否则算的时间太长。这图片是 800*450。

Python学习笔记17:玩转千图成像_第2张图片

首先得准备多张图片。

第一步:抓取百度图片

我的这篇文章详细说明了爬取百度图片的一些思路。

https://blog.csdn.net/weixin_42703239/article/details/111888324

这里直接上代码。

getBaiduImg.py

该文件的目的是,抓取百度搜索缩略图,并修改为 50*50 大小的图片,放到 img 文件夹中。同时,以时间戳进行重命名。

# -*- coding:utf-8 -*-
import requests
import re, time, datetime
import os
import random
import urllib.parse
from PIL import Image   # 导入一个模块

# 设置headers 为了防止反扒,设置多个headers
# chrome,firefox,Edge
headers = [
    {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
        'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
        'Connection': 'keep-alive'
    },
    {
        "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0',
        'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
        'Connection': 'keep-alive'
    },
    {
        "User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041',
        'Accept-Language': 'zh-CN',
        'Connection': 'keep-alive'
    }
]

picList = []   # 存储图片的空 List

keyword = input("请输入搜索的关键词:")
kw = urllib.parse.quote(keyword)  # 转码

# 获取 1000 张百度搜索出来的缩略图 list
def getPicList(kw, n):
    global picList
    weburl = r"https://image.baidu.com/search/acjson?tn=resultjson_com&logid=11601692320226504094&ipn=rj&ct=201326592&is=&fp=result&queryWord={kw}&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=&z=&ic=&hd=&latest=©right=&word={kw}&s=&se=&tab=&width=&height=&face=&istype=&qc=&nc=1&fr=&expermode=&force=&cg=girl&pn={n}&rn=30&gsm=1e&1611751343367=".format(kw=kw, n=n*30)
    req = requests.get(url=weburl, headers=random.choice(headers))
    req.encoding = req.apparent_encoding  # 防止中文乱码
    webJSON = req.text
    imgurlReg = '"thumbURL":"(.*?)"'   # 正则
    picList = picList + re.findall(imgurlReg, webJSON, re.DOTALL | re.I)

for i in range(150):   # 循环数比较大,如果实际上没有这么多图,那么 picList 数据不会增加。
    getPicList(kw, i)

for item in picList:
    # 后缀名 和名字
    itemList = item.split(".")
    hz = "."+itemList[-1]
    picName = str(int(time.time()*1000))   # 毫秒级时间戳
    # 请求图片
    imgReq = requests.get(url=item, headers=random.choice(headers))
    # 保存图片
    with open("img/" + picName + hz, "wb") as f:
        f.write(imgReq.content)
    #  用 Image 模块打开图片
    im = Image.open("img/" + picName + hz)
    bili = im.width / im.height  # 获取宽高比例,根据宽高比例调整图片大小
    newIm = None
    # 调整图片的大小,最小的一边设置为 50
    if bili >= 1:
        newIm = im.resize((round(bili*50), 50))
    else:
        newIm = im.resize((50, round(50*im.height/im.width) ))
    # 截取图片中 50*50 的部分
    clip = newIm.crop((0, 0, 50, 50))  # 截取图片,crop 裁切
    clip.convert("RGB").save("img/" + picName + hz)  # 保存截取的图片
    print(picName + hz + " 处理完毕")

第二步:生成千图成像

# -*- coding:utf-8 -*-
from PIL import Image
import os
import numpy as np

# 获取图像的平均颜色值
def compute_mean(imgPath):
    '''
    获取图像平均颜色值
    :param imgPath: 缩略图路径
    :return: (r,g,b)整个缩略图的rgb平均值
    '''
    im = Image.open(imgPath)
    im = im.convert("RGB")  # 转为 rgb模式
    # 把图像数据转为数据序列。以行为单位,每行存储每个像素点的色彩
    '''如:
     [[ 60  33  24] 
      [ 58  34  24]
      ...
      [188 152 136] 
      [ 99  96 113]]
      
     [[ 60  33  24] 
      [ 58  34  24]
      ...
      [188 152 136] 
      [ 99  96 113]]
    '''
    imArray = np.array(im)
    # mean()函数功能:求指定数据的取均值
    R = np.mean(imArray[:, :, 0])  # 获取所有 R 值的平均值
    G = np.mean(imArray[:, :, 1])
    B = np.mean(imArray[:, :, 2])
    return (R, G, B)

def getImgList():
    """
    获取缩略图的路径及平均色彩
    :return: list,存储了图片路径、平均色彩值。
    """
    imgList = []
    for pic in os.listdir("img/"):
        imgPath = "img/" + pic
        imgRGB = compute_mean(imgPath)
        imgList.append({
            "imgPath": imgPath,
            "imgRGB": imgRGB
        })
    return imgList

def computeDis(color1, color2):
    '''
    计算两张图的颜色差,计算机的是色彩空间距离。
    dis = (R**2 + G**2 + B**2)**0.5
    参数:color1,color2 是色彩数据 (r,g,b)
    '''
    dis = 0
    for i in range(len(color1)):
        dis += (color1[i]-color2[i])**2
    dis = dis**0.5
    return dis


def create_image(bgImg, imgDir, N=10, M=50):
    '''
    根据背景图,用头像填充出新图
    bgImg:背景图地址
    imgDir:头像目录
    N:背景图缩放的倍率
    M:头像的大小(MxM)
    '''
    # 获取图片列表
    imgList = getImgList()

    # 读取图片
    bg = Image.open(bgImg)
    # bg = bg.resize((bg.size[0] // N, bg.size[1] // N))  # 缩放。建议缩放下原图,图片太大运算时间很长。
    bgArray = np.array(bg)
    width = bg.size[0] * M  # 新生成图片的宽度。每个像素倍放大 M 倍
    height = bg.size[1] * M  # 新生成图片的高度

    # 创建空白的新图
    newImg = Image.new('RGB', (width, height))

    # 循环填充图
    for x in range(bgArray.shape[0]):      # x,行数据,可以用原图宽替代
        for y in range(bgArray.shape[1]):  # y,列数据,,可以用原图高替代
            # 找到距离最小的图片
            minDis = 10000
            index = 0
            for img in imgList:
                dis = computeDis(img['imgRGB'], bgArray[x][y])
                if dis < minDis:
                    index = img['imgPath']
                    minDis = dis
            # 循环完毕,index 就是存储了色彩最相近的图片路径
            #         minDis 存储了色彩差值
            # 填充
            tempImg = Image.open(index)       # 打开色差距离最小的图片
            # 调整图片大小,此处可以不调整,因为我在下载图的时候就已经调整好了
            tempImg = tempImg.resize((M, M))
            # 把小图粘贴到新图上。注意 x,y ,行列不要搞混了。相距 M 粘贴一张。
            newImg.paste(tempImg, (y * M, x * M))
            print('(%d, %d)' % (x, y))    # 打印进度。格式化输出 x,y

    # 保存图片
    newImg.save('final.jpg')    # 最后保存图片

create_image('pic2.jpg','img/')

这一步是关键,简单说明下:

1. 获取图像的颜色平均值,用到了 numpy 的 mean 方法。

2. 获取所有的缩略图,用到了 os 的 listdir 方法。

3. 计算颜色差,考虑到颜色是 rgb 三色,就用了色彩空间求差距的方法。这个方法类似在立体空间里,求两点的距离----(x,y,z)三个坐标的差值平方和,再开方。

4. 创建图像,利用循环遍历,获取原图色彩值,在缩略图中找与之色差最小的图;把该图粘贴到新图里面,粘贴的坐标位置要考虑到缩略图的大小 50*50 。

看似复杂,算法其实蛮简单的,就是对一些关键函数要理解到位。

最终效果:

 

你可能感兴趣的:(python,python)