热门标签 | 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效果 


推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了在Vue项目中如何结合Element UI解决连续上传多张图片及图片编辑的问题。作者强调了在编码前要明确需求和所需要的结果,并详细描述了自己的代码实现过程。 ... [详细]
  • 006_Redis的List数据类型
    1.List类型是一个链表结构的集合,主要功能有push,pop,获取元素等。List类型是一个双端链表的结构,我们可以通过相关操作进行集合的头部或者尾部添加删除元素,List的设 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • IjustinheritedsomewebpageswhichusesMooTools.IneverusedMooTools.NowIneedtoaddsomef ... [详细]
  • 突破MIUI14限制,自定义胶囊图标、大图标样式,支持任意APP
    本文介绍了如何突破MIUI14的限制,实现自定义胶囊图标和大图标样式,并支持任意APP。需要一定的动手能力和主题设计师账号权限或者会主题pojie。详细步骤包括应用包名获取、素材制作和封包获取等。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
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社区 版权所有