热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

颜色迁移(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&#61;[None], ref_mask_lists&#61;[None]):ref_img_lab &#61; cv2.cvtColor(ref_img, cv2.COLOR_BGR2LAB)in_img_lab &#61; cv2.cvtColor(in_img, cv2.COLOR_BGR2LAB)in_avg &#61; np.ones(in_img.shape, np.float32)in_std &#61; np.ones(in_img.shape, np.float32)ref_avg &#61; np.ones(in_img.shape, np.float32)ref_std &#61; np.ones(in_img.shape, np.float32)mask_all &#61; 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 &#61; cv2.meanStdDev(in_img_lab, mask&#61;in_mask)np.copyto(in_avg, in_avg_tmp.reshape(1,1,-1), where&#61;np.expand_dims(in_mask,2)!&#61;0) #numpy.copyto(destination, source)np.copyto(in_std, in_std_tmp.reshape(1,1,-1), where&#61;np.expand_dims(in_mask,2)!&#61;0) ref_avg_tmp, ref_std_tmp &#61; cv2.meanStdDev(ref_img_lab, mask&#61;ref_mask)np.copyto(ref_avg, ref_avg_tmp.reshape(1,1,-1), where&#61;np.expand_dims(in_mask,2)!&#61;0) #numpy.copyto(destination, source)np.copyto(ref_std, ref_std_tmp.reshape(1,1,-1), where&#61;np.expand_dims(in_mask,2)!&#61;0) #maskmask_all[in_mask!&#61;0] &#61; 1in_std[in_std&#61;&#61;0] &#61;1 #避免除数为0的情况transfered_lab &#61; (in_img_lab - in_avg)/(in_std) *ref_std &#43; ref_avg transfered_lab[transfered_lab<0] &#61; 0transfered_lab[transfered_lab>255] &#61; 255out_img &#61; 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&#61;mask_all&#61;&#61;0) return out_img"""
#img1 &#61; cv2.imread("imgs/1.png")
#img2 &#61; cv2.imread("imgs/2.png")
#img1 &#61; cv2.imread("welsh22/1.png", 1)
#img2 &#61; cv2.imread("welsh22/2.png", 1)
img1 &#61; cv2.imread("welsh22/gray.jpg", 1)
img2 &#61; 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 &#61; cv2.imread("ab.jpeg?s=#34;)
img2 &#61; cv2.imread("hsy.jpeg?s=#34;)
mask1 &#61; cv2.imread("ab_parsing.jpg", 0)
mask1[mask1<128]&#61;0
mask1[mask1>&#61;128]&#61;255
mask2 &#61; cv2.imread("hsy_parsing.jpg", 0)
mask2[mask2<128]&#61;0
mask2[mask2>&#61;128]&#61;255
cv2.imwrite("out.jpg", color_trans_reinhard(img1, img2, [mask1], [mask2]))

Welsh代码&#xff1a;

改进点&#xff0c;


  1. 主要是去掉for循环操作。
  2. 将计算一个领域内的std&#xff0c;使用均值滤波&#43;numpy实现近似替换。差别目测看不出。
  3. 修改参考图的weight&#xff0c;全部int化&#xff0c;只保留不一样的weight&#xff0c;实际测试大概150个左右的weight就可以。
  4. 修改最近weight查找思路&#xff0c;使用numpy减法操作&#43;argmin&#xff0c;替换2分查找。
  5. 整体速度比原始代码快18倍。

def get_domain_std(img_l, pixel, height, width, window_size):window_left &#61; max(pixel[1] - window_size, 0)window_right &#61; min(pixel[1] &#43; window_size &#43; 1, width)window_top &#61; max(pixel[0] - window_size, 0)window_bottom &#61; min(pixel[0] &#43; window_size &#43; 1, height)window_slice &#61; 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&#61;[None]):weight_list &#61; []pixel_a_list &#61; []pixel_b_list &#61; []ref_img_mask &#61; 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 &#61; np.bitwise_or(x, ref_img_mask)ref_img_l_mean &#61; cv2.blur(ref_img_l, (window_size, window_size))ref_img_l_std &#61; np.sqrt(cv2.blur(np.power((ref_img_l - ref_img_l_mean), 2), (window_size, window_size)))for _ in range(segment):height_index &#61; np.random.randint(ref_img_height)width_index &#61; np.random.randint(ref_img_width)pixel &#61; [height_index, width_index] #[x,y]if ref_img_mask[pixel[0], pixel[1]] &#61;&#61; 0:continuepixel_light &#61; ref_img_l[pixel[0], pixel[1]]pixel_a &#61; ref_img_a[pixel[0], pixel[1]]pixel_b &#61; ref_img_b[pixel[0], pixel[1]]#pixel_std &#61; get_domain_std(ref_img_l, pixel, ref_img_height, ref_img_width, window_size)pixel_std &#61; ref_img_l_std[height_index, width_index]weight_value &#61; int(pixel_light * ratio &#43; 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&#61;[None], ref_mask_lists&#61;[None]):start &#61; time.time()#参考图ref_img_height, ref_img_width, ref_img_channel &#61; ref_img.shapewindow_size&#61;5 #窗口大小segment&#61; 10000#随机点个数ratio&#61;0.5 #求weight的比例系数ref_img_lab &#61; cv2.cvtColor(ref_img, cv2.COLOR_BGR2Lab)ref_img_l, ref_img_a, ref_img_b &#61; cv2.split(ref_img_lab)#计算参考图weightref_img_weight_array, ref_img_pixel_a_array, ref_img_pixel_b_array &#61; 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 &#61; np.max(ref_img_l), np.min(ref_img_l)#输入图in_img_height, in_img_width, in_img_channel &#61; in_img.shapein_img_lab &#61; cv2.cvtColor(in_img, cv2.COLOR_BGR2LAB)# 获取灰度图像的亮度信息&#xff1b;in_img_l, in_img_a, in_img_b &#61; cv2.split(in_img_lab)in_img_max_pixel, in_img_min_pixel &#61; np.max(in_img_l), np.min(in_img_l)pixel_ratio &#61; (ref_img_max_pixel - ref_img_min_pixel) / (in_img_max_pixel - in_img_min_pixel)# 把输入图像的亮度值映射到参考图像范围内&#xff1b;in_img_l &#61; ref_img_min_pixel &#43; (in_img_l - in_img_min_pixel) * pixel_ratioin_img_l &#61; in_img_l.astype(np.uint8)in_img_l_mean &#61; cv2.blur(in_img_l, (window_size, window_size))in_img_l_std &#61; np.sqrt(cv2.blur(np.power((in_img_l - in_img_l_mean), 2), (window_size, window_size)))in_img_weight_pixel &#61; ratio * in_img_l &#43; (1 - ratio) * in_img_l_stdnearest_pixel_index &#61; np.argmin(np.abs(ref_img_weight_array.reshape(1,1,-1) - np.expand_dims(in_img_weight_pixel, 2)), axis&#61;2).astype(np.float32)in_img_a &#61; cv2.remap(ref_img_pixel_a_array.reshape(1, -1), nearest_pixel_index, np.zeros_like(nearest_pixel_index, np.float32), interpolation&#61;cv2.INTER_LINEAR)in_img_b &#61; cv2.remap(ref_img_pixel_b_array.reshape(1, -1), nearest_pixel_index, np.zeros_like(nearest_pixel_index, np.float32), interpolation&#61;cv2.INTER_LINEAR)merge_img &#61; cv2.merge([in_img_l, in_img_a, in_img_b])bgr_img &#61; cv2.cvtColor(merge_img, cv2.COLOR_LAB2BGR)mask_all &#61; 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 &#61; np.bitwise_or(x, mask_all)mask_all &#61; cv2.merge([mask_all, mask_all, mask_all])np.copyto(bgr_img, in_img, where&#61;mask_all&#61;&#61;0) end &#61; time.time()print("time", end-start)return bgr_imgif __name__ &#61;&#61; &#39;__main__&#39;:# 创建参考图像的分析类&#xff1b;#ref_img &#61; cv2.imread("consult.jpg")#ref_img &#61; cv2.imread("2.png")ref_img &#61; cv2.imread("../imgs/2.png")# 读取灰度图像&#xff1b;opencv默认读取的是3通道的&#xff0c;不需要我们扩展通道&#xff1b;#in_img &#61; cv2.imread("gray.jpg")#in_img &#61; cv2.imread("1.png")in_img &#61; cv2.imread("../imgs/1.png")bgr_img &#61; color_trans_welsh(in_img, ref_img)cv2.imwrite("out_ren.jpg", bgr_img)"""ref_img &#61; cv2.imread("../hsy.jpeg?s=#34;)ref_mask &#61; cv2.imread("../hsy_parsing.jpg", 0)ref_mask[ref_mask<128] &#61; 0ref_mask[ref_mask>&#61;128] &#61; 255in_img &#61; cv2.imread("../ab.jpeg?s=#34;)in_mask &#61; cv2.imread("../ab_parsing.jpg", 0)in_mask[in_mask<128] &#61; 0in_mask[in_mask>&#61;128] &#61; 255bgr_img &#61; color_trans_welsh(in_img, ref_img, in_mask_lists&#61;[in_mask], ref_mask_lists&#61;[ref_mask])cv2.imwrite("bgr.jpg", bgr_img)"""


效果对比&#xff1a;

从左到右&#xff0c;分别为原图&#xff0c;参考图&#xff0c;reinhard效果&#xff0c;welsh效果 

 从左到右&#xff0c;分别为原图&#xff0c;原图皮肤mask&#xff0c;参考图&#xff0c;参考图皮肤mask&#xff0c;reinhard效果&#xff0c;welsh效果 


推荐阅读
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文基于刘洪波老师的《英文词根词缀精讲》,深入探讨了多个重要词根词缀的起源及其相关词汇,帮助读者更好地理解和记忆英语单词。 ... [详细]
  • 本教程涵盖OpenGL基础操作及直线光栅化技术,包括点的绘制、简单图形绘制、直线绘制以及DDA和中点画线算法。通过逐步实践,帮助读者掌握OpenGL的基本使用方法。 ... [详细]
  • 基因组浏览器中的Wig格式解析
    本文详细介绍了Wiggle(Wig)格式及其在基因组浏览器中的应用,涵盖variableStep和fixedStep两种主要格式的特点、适用场景及具体使用方法。同时,还提供了关于数据值和自定义参数的补充信息。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 本文详细介绍了Java中org.neo4j.helpers.collection.Iterators.single()方法的功能、使用场景及代码示例,帮助开发者更好地理解和应用该方法。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 导航栏样式练习:项目实例解析
    本文详细介绍了如何创建一个具有动态效果的导航栏,包括HTML、CSS和JavaScript代码的实现,并附有详细的说明和效果图。 ... [详细]
  • 本文介绍如何使用 Python 编写程序,检查给定列表中的元素是否形成交替峰值模式。我们将探讨两种不同的方法来实现这一目标,并提供详细的代码示例。 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文探讨了如何优化和正确配置Kafka Streams应用程序以确保准确的状态存储查询。通过调整配置参数和代码逻辑,可以有效解决数据不一致的问题。 ... [详细]
  • dotnet 通过 Elmish.WPF 使用 F# 编写 WPF 应用
    本文来安利大家一个有趣而且强大的库,通过F#和C#混合编程编写WPF应用,可以在WPF中使用到F#强大的数据处理能力在GitHub上完全开源Elmis ... [详细]
  • 在现代Web应用中,当用户滚动到页面底部时,自动加载更多内容的功能变得越来越普遍。这种无刷新加载技术不仅提升了用户体验,还优化了页面性能。本文将探讨如何实现这一功能,并介绍一些实际应用案例。 ... [详细]
author-avatar
lmc的
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有