写在前面的话。
之前在码友slandarer的CSDN主页https://blog.csdn.net/slandarer看到他用MATLAB实现了几个有趣的图片处理趣案例,一时技痒,斗胆留言我也要用python跟帖。
今天兑现诺言,小试牛刀,就从“Python制作抖音同款含褶皱面料图”开始吧,有兴趣的同学可以先看slandarer的原帖https://blog.csdn.net/slandarer/article/details/115709803
这是我第一次在CSDN发帖,有不当的地方请各位大神轻拍指正。
为方便大家对比学习两种语言的实现方法,以下基本仿照原帖行文结构展开。
先看效果!!
右下角原帖图像的水印都还在
要用到numpy和opencv两个站点包。当然,要说明的是,我使用的是python3.7.6环境,编辑器推荐Anaconda3,需要的请自行移步清华大学镜像源下载。
Anaconda3自带numpy包,opencv需要自行安装,方法:在命令提示行键入pip install opencv-python,回车,坐等安装完毕即可。
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 24 22:55:24 2021
@author: ck
"""
import numpy as np
import cv2
导入褶皱图片(background.jpg)作为背景图像,蒙皮图片(foreground.jpg)作为前景;将褶皱图片灰度化,将前景图调整至与褶皱图片相同大小:
#1.读取背景和前景图像
bimg = cv2.imread('background.jpg')
bgray = cv2.cvtColor(bimg, cv2.COLOR_BGR2GRAY).astype(np.int)
fimg0 = cv2.imread('foreground.jpg')
fimg = cv2.resize(fimg0, dsize = bgray.shape, interpolation=cv2.INTER_NEAREST).astype(np.int)
background.jpg
foreground.jpg
因为我们要对前景图片进行拉伸,难免边角处缺一块,因此我们首先将边缘处颜色往外扩展几圈(13圈)
代码:
#2.构造扩展的前景图像(扩大13圈)
Y,X,N = fimg.shape
exfimg = np.zeros((Y+26,X+26,N))
exfimg[13:-13, 13:-13, :] = fimg#中心
for i in range(13):#左右,不含四角
exfimg[13:-13, i, :] = fimg[:,0,:]#左
exfimg[13:-13, -i-1, :] = fimg[:,-1,:]#右
for i in range(13):#上下,含四角
exfimg[i, :, :] = exfimg[13,:,:]#上
exfimg[-i-1, :, :] = exfimg[-14,:,:]#下
效果:
(本部分文字说明部分除括号里部分,全部来自slandarer原帖,因为我实在没有参透扭曲置换的原理到底是啥,所以这部分只当了数学处理过程的搬运工)
原理借鉴ps扭曲置换的原理,亮度较大的像素(大于128)取右下角像素RGB值进行置换,亮度较小的像素(小于128)取左上角像素RGB值进行置换,由于
(255-128)/10=12.7
(0-128)/10=-12.8
各个像素点与替换像素点的距离不超过13(看slandarer给出的代码,我觉得这里应该是26,替换像素点在原像素点右下45°方向距离26范围以内),因此上一步共扩展了13圈。
同时因为各个像素分布为整数点位置,而位置差计算一般都不是整数,因此我们要对偏移距离向上向下取整,获得两个像素点RGB值,并对这两点数值进行线性插值即可
代码:
#3.像素映射
goffset = (bgray - 128)/10
offsetLim1 = (np.floor(goffset) + 13).astype(np.int)
offsetLim2 = (np.ceil(goffset) + 13).astype(np.int)
sep1 = (goffset - np.floor(goffset)).flatten()
sep2 = (np.ceil(goffset) - goffset).flatten()
XX, YY = np.meshgrid(range(exfimg.shape[0]-26),range(exfimg.shape[1]-26))
XX1, YY1 = XX + offsetLim1, YY + offsetLim1#向下取整坐标矩阵
XX2, YY2 = XX + offsetLim2, YY + offsetLim2#向上取整坐标矩阵
c1 = exfimg[YY1.flatten(), XX1.flatten(), :]
c2 = exfimg[YY2.flatten(), XX2.flatten(), :]
p1 = np.where(sep1 == 0, c1[:,0], c2[:,0]*sep1.flatten() + c1[:,0]*sep2.flatten())
p2 = np.where(sep1 == 0, c1[:,0], c2[:,1]*sep1.flatten() + c1[:,1]*sep2.flatten())
p3 = np.where(sep1 == 0, c1[:,0], c2[:,2]*sep1.flatten() + c1[:,2]*sep2.flatten())
(同理,本部分文字全部来自slandarer原帖,我只是数学处理过程的搬运工)
将两张图片叠加起来
公式:混合色×基色 / 255=结果色
由于正片叠底后所出图片较暗,这里我们选择除以220而不是255:
代码:
#4.正片叠底
newarr = np.array([p1.reshape(bgray.shape),p2.reshape(bgray.shape),p3.reshape(bgray.shape)])
newarr = newarr*bgray/220
newarr = newarr.transpose((1,2,0)).astype(np.uint8)
cv2.imwrite('result.jpg', newarr)
cv2.imshow('add_result',newarr)
效果见本帖开头。
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 24 22:55:24 2021
@author: caokun
"""
import numpy as np
import cv2
#1.读取背景和前景图像
bimg = cv2.imread('background.jpg')
bgray = cv2.cvtColor(bimg, cv2.COLOR_BGR2GRAY).astype(np.int)
fimg0 = cv2.imread('foreground.jpg')
fimg = cv2.resize(fimg0, dsize = bgray.shape, interpolation=cv2.INTER_NEAREST).astype(np.int)
#2.构造扩展的前景图像(扩大13圈)
Y,X,N = fimg.shape
exfimg = np.zeros((Y+26,X+26,N))
exfimg[13:-13, 13:-13, :] = fimg#中心
for i in range(13):#左右,不含四角
exfimg[13:-13, i, :] = fimg[:,0,:]#左
exfimg[13:-13, -i-1, :] = fimg[:,-1,:]#右
for i in range(13):#上下,含四角
exfimg[i, :, :] = exfimg[13,:,:]#上
exfimg[-i-1, :, :] = exfimg[-14,:,:]#下
cv2.imwrite('expandimg.jpg', exfimg)
#3.像素映射
goffset = (bgray - 128)/10
offsetLim1 = (np.floor(goffset) + 13).astype(np.int)
offsetLim2 = (np.ceil(goffset) + 13).astype(np.int)
sep1 = (goffset - np.floor(goffset)).flatten()
sep2 = (np.ceil(goffset) - goffset).flatten()
XX, YY = np.meshgrid(range(exfimg.shape[0]-26),range(exfimg.shape[1]-26))
XX1, YY1 = XX + offsetLim1, YY + offsetLim1#向下取整坐标矩阵
XX2, YY2 = XX + offsetLim2, YY + offsetLim2#向上取整坐标矩阵
c1 = exfimg[YY1.flatten(), XX1.flatten(), :]
c2 = exfimg[YY2.flatten(), XX2.flatten(), :]
p1 = np.where(sep1 == 0, c1[:,0], c2[:,0]*sep1.flatten() + c1[:,0]*sep2.flatten())
p2 = np.where(sep1 == 0, c1[:,0], c2[:,1]*sep1.flatten() + c1[:,1]*sep2.flatten())
p3 = np.where(sep1 == 0, c1[:,0], c2[:,2]*sep1.flatten() + c1[:,2]*sep2.flatten())
#4.正片叠底
newarr = np.array([p1.reshape(bgray.shape),p2.reshape(bgray.shape),p3.reshape(bgray.shape)])
newarr = newarr*bgray/220
newarr = newarr.transpose((1,2,0)).astype(np.uint8)
cv2.imwrite('result.jpg', newarr)
cv2.imshow('add_result',newarr)
说明:
我对前景图像拓展部分的处理逻辑做了优化,看起来要优雅一点(python本身就以优雅著称嘛);
总代码量(有效部分)比原帖略少,像素映射部分还可以精简优化,没时间搞了,有兴趣的朋友可以试试;
整体速度与原贴提供的MATLAB代码不相上下(也可能略慢)。
各位斧正。