python|基于移动最小二乘(MLS)的图像扭曲刚性变形python实现


基于移动最小二乘(MLS)的图像扭曲刚性变形python实现

  • 简单介绍一下基于mls的图像变形
    • 直接上代码
    • 用来做的一个瘦脸前后对比
    • 写在后面

简单介绍一下基于mls的图像变形 先假设我们的图片像素为h乘w,这个算法的一个流程是:先选择图像上面的一些控制点,比如我选中人脸五官的点,那我们现在就有了这几个控制点的坐标和一个包含h乘w个图片像素点坐标的矩阵,使用这些数据就可以预先计算出一系列的中间变量,之后就可以输入图像变形后控制点的坐标位置(换种说法就是通过改变控制点控制图像变形)计算得到另一个包含h乘以w个坐标的矩阵,这个矩阵每个元素对应原来的矩阵每个元素。最后把原图像坐标的信息一一对应地插值到现在这个矩阵的坐标位置完成图像变形。
直接上代码 import numpy as np
import cv2
class trans():
def __init__(self, img, pi): width, height = img.shape[:2] pcth = np.repeat(np.arange(height).reshape(height, 1), [width], axis=1) pctw = np.repeat(np.arange(width).reshape(width, 1), [height], axis=1).Tself.img_coordinate = np.swapaxes(np.array([pcth, pctw]), 1, 2).T self.cita = compute_G(self.img_coordinate, pi, height, width) self.pi = pi self.W, self.A, self.Z = pre_compute_waz(self.pi, height, width, self.img_coordinate) self.height = height self.width = widthdef deformation(self, img, qi):qi = self.pi * 2 - qi mapxy = np.swapaxes(np.float32(compute_fv(qi, self.W, self.A, self.Z, self.height, self.width, self.cita, self.img_coordinate)), 0, 1) img = cv2.remap(img, mapxy[:, :, 0], mapxy[:, :, 1], borderMode=cv2.BORDER_WRAP, interpolation=cv2.INTER_LINEAR)return img

def pre_compute_waz(pi, height, width, img_coordinate):
''':param pi: :param height: :param width: :param img_coordinate: 坐标信息矩阵 :return: '''# height*width*控制点个数 wi = np.reciprocal(np.power(np.linalg.norm(np.subtract(pi, img_coordinate.reshape(height, width, 1, 2)) + 0.000000001, axis=3),2))# height*width*2 pstar = np.divide(np.matmul(wi,pi), np.sum(wi, axis=2).reshape(height,width,1))# height*width*控制点个数*2 phat = np.subtract(pi, pstar.reshape(height, width, 1, 2))z1 = np.subtract(img_coordinate, pstar) z2 = np.repeat(np.swapaxes(np.array([z1[:,:,1], -z1[:,:,0]]), 1, 2).T.reshape(height,width,1,2,1), [pi.shape[0]], axis=2)# height*width*控制点个数*2*1 z1 = np.repeat(z1.reshape(height,width,1,2,1), [pi.shape[0]], axis=2)# height*width*控制点个数*1*2 s1 = phat.reshape(height,width,pi.shape[0],1,2) s2 = np.concatenate((s1[:,:,:,:,1], -s1[:,:,:,:,0]), axis=3).reshape(height,width,pi.shape[0],1,2)a = np.matmul(s1, z1) b = np.matmul(s1, z2) c = np.matmul(s2, z1) d = np.matmul(s2, z2)# 重构wi形状 ws = np.repeat(wi.reshape(height,width,pi.shape[0],1),[4],axis=3)# height*width*控制点个数*2*2 A = (ws * np.concatenate((a,b,c,d), axis=3).reshape(height,width,pi.shape[0],4)).reshape(height,width,pi.shape[0],2,2)return wi, A, z1

def compute_fv(qi, W, A, Z, height, width, cita, img_coordinate):
‘’’
:param qi: :param W: :param A: :param Z: :param height: :param width: :param cita: 衰减系数,减少局部变形对整体的影响 :param img_coordinate: :return: '''qstar = np.divide(np.matmul(W,qi), np.sum(W, axis=2).reshape(height,width,1))qhat = np.subtract(qi, qstar.reshape(height, width, 1, 2)).reshape(height, width, qi.shape[0], 1, 2)fv_ = np.sum(np.matmul(qhat, A),axis=2)fv = np.linalg.norm(Z[:,:,0,:,:],axis=2) / (np.linalg.norm(fv_,axis=3)+0.0000000001) * fv_[:,:,0,:] + qstarfv = (fv - img_coordinate) * cita.reshape(height, width, 1) + img_coordinatereturn fv

def compute_G(img_coordinate, pi, height, width, thre = 0.7):
‘’’
衰减系数计算
:param img_coordinate:
:param pi:
:param height:
:param width:
:param thre: 影响系数,数值越大对控制区域外影响越大,反之亦然,取值范围0到无穷大
:return:
‘’’
max = np.max(pi, 0) min = np.min(pi, 0)length = np.max(max - min)# 计算控制区域中心 # p_ = (max + min) // 2 p_ = np.sum(pi,axis=0) // pi.shape[0]# 计算控制区域 minx, miny = min - length maxx, maxy = max + length minx = minx if minx > 0 else 0 miny = miny if miny > 0 else 0 maxx = maxx if maxx < height else height maxy = maxy if maxy < width else widthk1 =(p_ - [0,0])[1] / (p_ - [0,0])[0] k2 =(p_ - [height,0])[1] / (p_ - [height,0])[0] k4 =(p_ - [0,width])[1] / (p_ - [0,width])[0] k3 =(p_ - [height, width])[1] / (p_ - [height, width])[0] k = (np.subtract(p_, img_coordinate)[:, :, 1] / (np.subtract(p_, img_coordinate)[:, :, 0] + 0.000000000001)).reshape(height, width, 1) k = np.concatenate((img_coordinate, k), axis=2)k[:,:p_[1],0][(k[:,:p_[1],2] > k1) | (k[:,:p_[1],2] < k2)] = (np.subtract(p_[1], k[:,:,1]) / p_[1]).reshape(height, width, 1)[:,:p_[1],0][(k[:,:p_[1],2] > k1) | (k[:,:p_[1],2] < k2)] k[:,p_[1]:,0][(k[:,p_[1]:,2] > k3) | (k[:,p_[1]:,2] < k4)] = (np.subtract(k[:,:,1], p_[1]) / (width - p_[1])).reshape(height, width, 1)[:,p_[1]:,0][(k[:,p_[1]:,2] > k3) | (k[:,p_[1]:,2] < k4)] k[:p_[0],:,0][(k1 >= k[:p_[0],:,2]) & (k[:p_[0],:,2] >= k4)] = (np.subtract(p_[0], k[:,:,0]) / p_[0]).reshape(height, width, 1)[:p_[0],:,0][(k1 >= k[:p_[0],:,2]) & (k[:p_[0],:,2] >= k4)] k[p_[0]:,:,0][(k3 >= k[p_[0]:,:,2]) & (k[p_[0]:,:,2] >= k2)] = (np.subtract(k[:,:,0], p_[0]) / (height - p_[0])).reshape(height, width, 1)[p_[0]:,:,0][(k3 >= k[p_[0]:,:,2]) & (k[p_[0]:,:,2] >= k2)]cita = np.exp(-np.power(k[:,:,0] / thre,2)) cita[minx:maxx,miny:maxy] = 1 # 如果不需要局部变形,可以把cita的值全置为1 # cita = 1return cita

if name == ‘main’:
# 里面输入你的图片位置,绝对位置和相对位置都可以 img = cv2.imread('clonepic.jpg') pi = np.array([208, 536, 280, 769, 516, 877, 661, 709, 656, 489]).reshape(-1, 2) qi = np.array([208, 536, 250, 769, 516, 877, 661, 709, 656, 489]).reshape(-1, 2)ddd = trans(img, pi) img = ddd.deformation(img, qi)cv2.namedWindow("bianxing", 0) cv2.resizeWindow("bianxing", 640, 480) cv2.imshow('bianxing', img) cv2.waitKey(0)

用来做的一个瘦脸前后对比 上面张图片是瘦脸后,下面张瘦脸前
python|基于移动最小二乘(MLS)的图像扭曲刚性变形python实现
文章图片
python|基于移动最小二乘(MLS)的图像扭曲刚性变形python实现
文章图片

写在后面 因为是用python写了一个人脸自动美形的毕设,查了好多都没找到使用python写的变形算法,所以自己写了一个,感觉应该还是会有人需要这个的,所以把代码放上来。然后我会把整个毕设放到另一篇博客,有需要的自行翻阅
【python|基于移动最小二乘(MLS)的图像扭曲刚性变形python实现】因为我目前是没有做程序相关的工作,而是选择了做摄影自媒体。如果这篇文章有帮助到你或者如果你也对摄影感兴趣,请支持关注一下我的公众号:镜头一锅粥。在此万分感谢。

    推荐阅读