Python 图像处理之颜色迁移(reinhard VS welsh)

目录
  • 前言
  • 应用场景
  • 出发点
  • reinhard算法流程
  • welsh算法流程
  • Reinhard VS welsh
  • 代码实现
    • Reinhard
    • Welsh代码
  • 效果对比

前言

reinhard算法:Color Transfer between Images,作者Erik Reinhard

welsh算法:Transferring Color to Greyscale Images,作者Tomihisa Welsh

应用场景

人像图换肤色,风景图颜色迁移

出发点

  1. RGB三通道有很强的关联性,而做颜色的改变同时恰当地改变三通道比较困难。
  2. 需要寻找三通道互不相关的也就是正交的颜色空间,作者想到了Ruderman等人提出的lαβ颜色空间。三个轴向正交意味着改变任何一个通道都不影响其他通道,从而能够较好的保持原图的自然效果。三个通道分别代表:亮度,黄蓝通道,红绿通道。

reinhard算法流程

  1. 输入变换图,颜色参考图,将其都从bgr空间转化为lab空间
  2. 分别计算变换图,参考图在lab空间的均值,方差
  3. (变换图lab - 变换图均值)/变换图方差 *参考图方差 + 参考图均值
  4. 变换图lab空间转化为bgr空间,输出结果

welsh算法流程

  1. 输入变换图,颜色参考图,将其都从bgr空间转化为lab空间
  2. 定义随机参考点个数segment,领域空间大小window_size,加权系数ratio。从参考图片中随机选择segment个样本点,将这些样本点的像素亮度值L和L空间window_size领域内得方差σ保存起来,求这2个的加权W,W = L* ratio+ σ*(1-ratio)。这样就可以得到segment个W,以及与其一一对应的a通道,b通道对应位置的数值。
  3. 对变换图的L通道基于颜色参考图的L通道进行亮度重映射,保证后续的像素匹配正确进行
  4. 对变换图进行逐像素扫描,对每个像素,计算其权值W,计算方式和上面一样。然后在第二步得到的样本点中找到与其权值最接近的参考点,并将该点的a通道和b通道的值赋给变换图的a通道和b通道。
  5. 将变换图从Lab空间转化到bgr空间。

Reinhard VS welsh

  1. Reinhard 操作简单,高效,速度快很多。
  2. welsh算法涉及到了参考图的W的计算,如果是参考图固定且已知的场景,这一步可以放入初始化中。如果不是这样的场景,那么这一步的计算也是很费时的。
  3. welsh整体速度慢很多,主要由于求方差造成。
  4. welsh的输出效果,受随机参考点个数以及位置的影响,每次的结果都会有差异。
  5. welsh的效果会有种涂抹不均匀的感觉,Reinhard 则没有这种问题。

代码实现

Reinhard

def color_trans_reinhard(in_img, ref_img, in_mask_lists=[None], ref_mask_lists=[None]):
    ref_img_lab = cv2.cvtColor(ref_img, cv2.COLOR_BGR2LAB)
    in_img_lab = cv2.cvtColor(in_img, cv2.COLOR_BGR2LAB)

    in_avg = np.ones(in_img.shape, np.float32)
    in_std = np.ones(in_img.shape, np.float32)
    ref_avg = np.ones(in_img.shape, np.float32)
    ref_std = np.ones(in_img.shape, np.float32)

    mask_all = np.zeros(in_img.shape, np.float32)
    for in_mask, ref_mask in zip(in_mask_lists, ref_mask_lists):
        #mask,取值为 0, 255, shape[height,width]
        in_avg_tmp, in_std_tmp = cv2.meanStdDev(in_img_lab, mask=in_mask)
        np.copyto(in_avg, in_avg_tmp.reshape(1,1,-1), where=np.expand_dims(in_mask,2)!=0) #numpy.copyto(destination, source)
        np.copyto(in_std, in_std_tmp.reshape(1,1,-1), where=np.expand_dims(in_mask,2)!=0) 

        ref_avg_tmp, ref_std_tmp = cv2.meanStdDev(ref_img_lab, mask=ref_mask)
        np.copyto(ref_avg, ref_avg_tmp.reshape(1,1,-1), where=np.expand_dims(in_mask,2)!=0) #numpy.copyto(destination, source)
        np.copyto(ref_std, ref_std_tmp.reshape(1,1,-1), where=np.expand_dims(in_mask,2)!=0) 

        #mask
        mask_all[in_mask!=0] = 1

    in_std[in_std==0] =1 #避免除数为0的情况
    transfered_lab = (in_img_lab - in_avg)/(in_std) *ref_std + ref_avg
    transfered_lab[transfered_lab<0] = 0
    transfered_lab[transfered_lab>255] = 255

    out_img = cv2.cvtColor(transfered_lab.astype(np.uint8), cv2.COLOR_LAB2BGR)

    if in_mask_lists[0] is not None and ref_mask_lists[0] is not None:
        np.copyto(out_img, in_img, where=mask_all==0) 

    return out_img

"""
#img1 = cv2.imread("imgs/1.png")
#img2 = cv2.imread("imgs/2.png")
#img1 = cv2.imread("welsh22/1.png", 1)
#img2 = cv2.imread("welsh22/2.png", 1)
img1 = cv2.imread("welsh22/gray.jpg", 1)
img2 = cv2.imread("welsh22/consult.jpg", 1)
cv2.imwrite("out.jpg", color_trans_reinhard(img1, img2, [np.ones(img1.shape[:-1],np.uint8)*255], [np.ones(img2.shape[:-1],np.uint8)*255]))
"""
img1 = cv2.imread("ab.jpeg")
img2 = cv2.imread("hsy.jpeg")
mask1 = cv2.imread("ab_parsing.jpg", 0)
mask1[mask1<128]=0
mask1[mask1>=128]=255
mask2 = cv2.imread("hsy_parsing.jpg", 0)
mask2[mask2<128]=0
mask2[mask2>=128]=255
cv2.imwrite("out.jpg", color_trans_reinhard(img1, img2, [mask1], [mask2]))

Welsh代码

改进点

  1. 主要是去掉for循环操作。
  2. 将计算一个领域内的std,使用均值滤波+numpy实现近似替换。差别目测看不出。
  3. 修改参考图的weight,全部int化,只保留不一样的weight,实际测试大概150个左右的weight就可以。
  4. 修改最近weight查找思路,使用numpy减法操作+argmin,替换2分查找。
  5. 整体速度比原始代码快18倍。
def get_domain_std(img_l, pixel, height, width, window_size):
    window_left = max(pixel[1] - window_size, 0)
    window_right = min(pixel[1] + window_size + 1, width)
    window_top = max(pixel[0] - window_size, 0)
    window_bottom = min(pixel[0] + window_size + 1, height)

    window_slice = img_l[window_top: window_bottom, window_left: window_right]

    return np.std(window_slice)

def get_weight_pixel(ref_img_l, ref_img_a, ref_img_b, ref_img_height, ref_img_width, segment, window_size, ratio, ref_mask_lists=[None]):
    weight_list = []
    pixel_a_list = []
    pixel_b_list = []

    ref_img_mask  = np.ones((ref_img_height, ref_img_width), np.uint8)
    if ref_mask_lists[0] is not None:
        for x in ref_mask_lists:
            ref_img_mask = np.bitwise_or(x, ref_img_mask)

    ref_img_l_mean = cv2.blur(ref_img_l, (window_size, window_size))
    ref_img_l_std = np.sqrt(cv2.blur(np.power((ref_img_l - ref_img_l_mean), 2),  (window_size, window_size)))
    for _ in range(segment):
        height_index = np.random.randint(ref_img_height)
        width_index = np.random.randint(ref_img_width)

        pixel = [height_index, width_index]  #[x,y]

        if ref_img_mask[pixel[0], pixel[1]] == 0:
            continue

        pixel_light = ref_img_l[pixel[0], pixel[1]]
        pixel_a = ref_img_a[pixel[0], pixel[1]]
        pixel_b = ref_img_b[pixel[0], pixel[1]]

        #pixel_std = get_domain_std(ref_img_l, pixel, ref_img_height, ref_img_width, window_size)
        pixel_std = ref_img_l_std[height_index, width_index]

        weight_value = int(pixel_light * ratio + pixel_std * (1 - ratio))
        if weight_value not in weight_list:
            weight_list.append(weight_value)
            pixel_a_list.append(pixel_a)
            pixel_b_list.append(pixel_b)                          

    return np.array(weight_list), np.array(pixel_a_list), np.array(pixel_b_list)

def color_trans_welsh(in_img, ref_img, in_mask_lists=[None], ref_mask_lists=[None]):
    start = time.time()
    #参考图
    ref_img_height, ref_img_width, ref_img_channel = ref_img.shape
    window_size=5 #窗口大小
    segment= 10000#随机点个数
    ratio=0.5     #求weight的比例系数

    ref_img_lab = cv2.cvtColor(ref_img, cv2.COLOR_BGR2Lab)
    ref_img_l, ref_img_a, ref_img_b = cv2.split(ref_img_lab)

    #计算参考图weight
    ref_img_weight_array, ref_img_pixel_a_array, ref_img_pixel_b_array =  get_weight_pixel(ref_img_l, ref_img_a, ref_img_b, ref_img_height, ref_img_width, segment, window_size, ratio, ref_mask_lists)

    ref_img_max_pixel, ref_img_min_pixel = np.max(ref_img_l), np.min(ref_img_l)

    #输入图
    in_img_height, in_img_width, in_img_channel = in_img.shape
    in_img_lab = cv2.cvtColor(in_img, cv2.COLOR_BGR2LAB)

    # 获取灰度图像的亮度信息;
    in_img_l, in_img_a, in_img_b = cv2.split(in_img_lab)

    in_img_max_pixel, in_img_min_pixel = np.max(in_img_l), np.min(in_img_l)
    pixel_ratio = (ref_img_max_pixel - ref_img_min_pixel) / (in_img_max_pixel - in_img_min_pixel)

    # 把输入图像的亮度值映射到参考图像范围内;
    in_img_l = ref_img_min_pixel + (in_img_l - in_img_min_pixel) * pixel_ratio
    in_img_l = in_img_l.astype(np.uint8)

    in_img_l_mean = cv2.blur(in_img_l, (window_size, window_size))
    in_img_l_std = np.sqrt(cv2.blur(np.power((in_img_l - in_img_l_mean), 2),  (window_size, window_size)))

    in_img_weight_pixel = ratio * in_img_l + (1 - ratio) * in_img_l_std

    nearest_pixel_index = np.argmin(np.abs(ref_img_weight_array.reshape(1,1,-1) - np.expand_dims(in_img_weight_pixel, 2)), axis=2).astype(np.float32)

    in_img_a = cv2.remap(ref_img_pixel_a_array.reshape(1, -1), nearest_pixel_index, np.zeros_like(nearest_pixel_index, np.float32), interpolation=cv2.INTER_LINEAR)
    in_img_b = cv2.remap(ref_img_pixel_b_array.reshape(1, -1), nearest_pixel_index, np.zeros_like(nearest_pixel_index, np.float32), interpolation=cv2.INTER_LINEAR)

    merge_img = cv2.merge([in_img_l, in_img_a, in_img_b])
    bgr_img = cv2.cvtColor(merge_img, cv2.COLOR_LAB2BGR)

    mask_all = np.zeros(in_img.shape[:-1], np.int32)
    if in_mask_lists[0] is not None and ref_mask_lists[0] is not None:
        for x in in_mask_lists:
            mask_all = np.bitwise_or(x, mask_all)
        mask_all = cv2.merge([mask_all, mask_all, mask_all])
        np.copyto(bgr_img, in_img, where=mask_all==0) 

    end = time.time()
    print("time", end-start)
    return bgr_img

if __name__ == '__main__':

    # 创建参考图像的分析类;
    #ref_img = cv2.imread("consult.jpg")
    #ref_img = cv2.imread("2.png")
    ref_img = cv2.imread("../imgs/2.png")

    # 读取灰度图像;opencv默认读取的是3通道的,不需要我们扩展通道;
    #in_img = cv2.imread("gray.jpg")
    #in_img = cv2.imread("1.png")
    in_img = cv2.imread("../imgs/1.png")

    bgr_img = color_trans_welsh(in_img, ref_img)
    cv2.imwrite("out_ren.jpg", bgr_img)
    """
    ref_img = cv2.imread("../hsy.jpeg")
    ref_mask = cv2.imread("../hsy_parsing.jpg", 0)
    ref_mask[ref_mask<128] = 0
    ref_mask[ref_mask>=128] = 255
    in_img = cv2.imread("../ab.jpeg")
    in_mask = cv2.imread("../ab_parsing.jpg", 0)
    in_mask[in_mask<128] = 0
    in_mask[in_mask>=128] = 255
    bgr_img = color_trans_welsh(in_img, ref_img, in_mask_lists=[in_mask], ref_mask_lists=[ref_mask])
    cv2.imwrite("bgr.jpg", bgr_img)
    """

效果对比

从左到右,分别为原图,参考图,reinhard效果,welsh效果 

 从左到右,分别为原图,原图皮肤mask,参考图,参考图皮肤mask,reinhard效果,welsh效果  

以上就是Python 图像处理之颜色迁移(reinhard VS welsh)的详细内容,更多关于Python 颜色迁移的资料请关注我们其它相关文章!

(0)

相关推荐

  • 浅谈python opencv对图像颜色通道进行加减操作溢出

    由于opencv读入图片数据类型是uint8类型,直接加减会导致数据溢出现象 (1)用Numpy操作 可以先将图片数据类型转换成int类型进行计算, data=np.array(image,dtype='int') 经过处理后(如:遍历,将大于255的置为255,小于0的置为0) 再将图片还原成uint8类型 data=np.array(image,dtype='uint8') 注意: (1)如果直接相加,那么 当像素值 > 255时,结果为对256取模的结果,例如:(240+66) % 256

  • python 实现图像快速替换某种颜色

    最近的对图像数据进行处理的时候需要将图像中的某个颜色替换为另一个颜色,但是网络上找到的方法都是通过对图像的遍历进行替换,实在是太费时了!刚开始使用时觉得CPU很快了,一张图片应该用不了多久,但是实际使用中耗时确实难以接受的!于是自己写了一个替换程序加快速度,比遍历快很多,但我觉得不是最快的,应该有通过矩阵索引更快的处理方式,只是我自己暂时并不知道该如何实现,如果以后能够实现会进行更新,暂时先写下自己暂时觉得可用的代码. 一.通过遍历替换 将图像中某个颜色替换为另一个颜色一般的做法是遍历整个图像,

  • Python图像处理之颜色的定义与使用分析

    本文实例讲述了Python图像处理之颜色的定义与使用.分享给大家供大家参考,具体如下: python中的颜色相关的定义在matplotlib模块中,为方便使用,这里给大家展示一下在这个模块中都定义了哪些选颜色. 1.颜色名称的导出 导出代码如下: import matplotlib for name, hex in matplotlib.colors.cnames.iteritems(): print(name, hex) 导出结果如下: names = { 'aliceblue':      

  • Python Opencv 通过轨迹(跟踪)栏实现更改整张图像的背景颜色

    !!!本博客,是对图像的背景颜色的修改的基础讲解~!!! 还包括一个练习--是对背景色修改的一点应用尝试!!!--始终相信学习多一点探索,脚步会更坚定一些~ 愿所有正在努力的人都可以坚持自己的路一直走下去! 实现轨迹(跟踪)栏功能的函数 函数主要参数讲解 cv.createTrackbar()--创建一个轨迹(跟踪)栏 cv.getTrackbarPos()--获取一个轨迹(跟踪)栏的值 cv.createTrackbar()参数如下: 参数一:trackbarname--轨迹(跟踪)栏名称 参

  • opencv-python 读取图像并转换颜色空间实例

    我就废话不多说了,直接上代码吧! #-*- encoding:utf-8 -*- ''' python 绘制颜色直方图 ''' import cv2 import numpy as np from matplotlib import pyplot as plt def readImage(): #读取图片 B,G,R,返回一个ndarray类型 #cv2.IMREAD_COLOR # 以彩色模式读入 1 #cv2.IMREAD_GRAYSCALE # 以灰色模式读入 0 img = cv2.im

  • Python 图像处理之颜色迁移(reinhard VS welsh)

    目录 前言 应用场景 出发点 reinhard算法流程 welsh算法流程 Reinhard VS welsh 代码实现 Reinhard Welsh代码 效果对比 前言 reinhard算法:Color Transfer between Images,作者Erik Reinhard welsh算法:Transferring Color to Greyscale Images,作者Tomihisa Welsh 应用场景 人像图换肤色,风景图颜色迁移 出发点 RGB三通道有很强的关联性,而做颜色的

  • Python图像处理之简单画板实现方法示例

    本文实例讲述了Python图像处理之简单画板实现方法.分享给大家供大家参考,具体如下: Python图像处理也是依赖opencv的Python接口实现的,Python语言简单易懂,简洁明了.本次实现画板涂鸦,一个是在里面画矩形,还有画线.其他也都可以扩展,本案例只做例程,思路是对鼠标事件的处理,以及滚动条调节颜色处理.鼠标事件就包含有左键按下,以及释放事件的处理. import cv2 import numpy as np # null function def nothing(x): pass

  • Python图像处理库PIL的ImageDraw模块介绍详解

    ImageDraw模块提供了图像对象的简单2D绘制.用户可以使用这个模块创建新的图像,注释或润饰已存在图像,为web应用实时产生各种图形. PIL中一个更高级绘图库见The aggdraw Module 一.ImageDraw模块的概念 1.  Coordinates 绘图接口使用和PIL一样的坐标系统,即(0,0)为左上角. 2.  Colours 为了指定颜色,用户可以使用数字或者元组,对应用户使用函数Image.new或者Image.putpixel.对于模式为"1","

  • Python图像处理库PIL中图像格式转换的实现

    在数字图像处理中,针对不同的图像格式有其特定的处理算法.所以,在做图像处理之前,我们需要考虑清楚自己要基于哪种格式的图像进行算法设计及其实现.本文基于这个需求,使用python中的图像处理库PIL来实现不同图像格式的转换. 对于彩色图像,不管其图像格式是PNG,还是BMP,或者JPG,在PIL中,使用Image模块的open()函数打开后,返回的图像对象的模式都是"RGB".而对于灰度图像,不管其图像格式是PNG,还是BMP,或者JPG,打开后,其模式为"L". 通

  • 基于python图像处理API的使用示例

    1.图像处理库 import cv2 as cv from PIL import * 常用的图像处理技术有图像读取,写入,绘图,图像色彩空间转换,图像几何变换,图像形态学,图像梯度,图像边缘检测,图像轮廓,图像分割,图像去噪,图像加水印以及修复水印等 2.opencv常用的接口 cv.imread() 读取图片,返回numpy cv.imwrite() 写入图片 cv.cvtColor() 图像色彩空间转换 cv.add() cv.subtract() cv.multiply() cv.divi

  • 超全Python图像处理讲解(多模块实现)

    Pillow模块讲解 一.Image模块 1.1 .打开图片和显示图片 对图片的处理最基础的操作就是打开这张图片,我们可以使用Image模块中的open(fp, mode)方法,来打开图片.open方法接收两个参数,第一个是文件路径,第二个是模式.主要的模式如下: mode(模式) bands(通道) 说明 "1" 1 数字1,表示黑白二值图片,每个像素用0或1共1位二进制码表示 "L" 1 灰度图 "P" 1 索引图 "RGB&quo

  • Python图像处理库PIL的ImageEnhance模块使用介绍

    ImageEnhance模块提供了一些用于图像增强的类. 一.ImageEnhance模块的接口 所有的增强类都实现了一个通用的接口,包括一个方法: enhancer.enhance(factor) ⇒ image 该方法返回一个增强过的图像.变量factor是一个浮点数,控制图像的增强程度.变量factor为1将返回原始图像的拷贝:factor值越小,颜色越少(亮度,对比度等),更多的价值.对变量facotr没有限制. 二.ImageEnhance模块的Color类 颜色增强类用于调整图像的颜

  • 解决python图像处理图像赋值后变为白色的问题

    用Python进行图像赋值,在1RGB基础上,加入光流两个通道,代码如下所示: import numpy as np import cv2 import matplotlib.pyplot as plt path = 'frame_00003_rgb.png' img = cv2.imread(path) img1 = np.zeros([480, 640, 5]) img1[:, :, 0:3] = np.array(img) cv2.imshow('test1', np.array(img)

  • Python图像处理之图像拼接

    一.前言 图像拼接技术就是将数张有重叠部分的图像(可能是不同时间.不同视角或者不同传感器获得的)拼成一幅无缝的全景图或高分辨率图像的技术. 二.特征点匹配 特征点具有局部差异性 动机:特征点具有局部差异性 图像梯度 Harris矩阵 以每个点为中心取一个窗口,窗口大小为55或者77,如果这个点具有差异性,往周围任意方向移动,周围的环境变化都是会比较大的,如果满足这个特性,我们就认为这个特征点具有明显的局部差异性.在工事中,I表示像素,如果是 彩色图像就是RGB,灰色图像就是灰度.(u,v)表示方

随机推荐