我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python图像处理之颜色迁移(reinhardVSwelsh)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python图像处理之颜色迁移(reinhardVSwelsh)

前言

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 颜色迁移的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python图像处理之颜色迁移(reinhardVSwelsh)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Pillow图像颜色处理怎么用

这篇文章将为大家详细讲解有关Pillow图像颜色处理怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Pillow 提供了颜色处理模块 ImageColor,该模块支持不同格式的颜色,比如 RGB 格式
2023-06-21

Android编程之图片颜色处理方法

本文实例讲述了Android编程之图片颜色处理方法。分享给大家供大家参考,具体如下: 你想做到跟美图秀秀一样可以处理自己的照片,美化自己的照片吗?其实你也可以自己做一个这样的软件,废话不多说了,直接上图,上代码了! 效果图如下: 没处理前:
2022-06-06

Python图像处理之模糊图像判断

这篇文章主要为大家详细介绍了Python图像处理中的模糊图像判断的实现,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
2022-12-08

Python图像处理之PIL库

本篇文章给大家带来了关于python的相关知识,其中主要整理了PIL库的相关问题,PIL库是一个具有强大图像处理能力的第三方库,不仅包含了丰富的像素、色彩操作功能,还可以用于图像归档和批量处理,下面一起来看一下,希望对大家有帮助。要点:PIL库是一个具有强大图像处理能力的第三方库,不仅包含了丰富的像素、色彩操作功能,还可以用于图像归档和批量处理。1.PIL库概述PIL(Python Image Li
2022-06-23

Python图像处理之图像清晰度评价

图像清晰度评价,顾名思义就是能够通过一张标准图,对同一组相机拍摄的照片进行清晰度评价。本文将利用Python实现这一效果,需要的可以参考一下
2022-12-08

Python图像处理之图像与视频处理基础教程

这篇文章主要介绍了Python图像处理之图像与视频处理基础教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-05-15

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录