热门标签 | 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开发能力。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 本文介绍了[从头学数学]中第101节关于比例的相关问题的研究和修炼过程。主要内容包括[机器小伟]和[工程师阿伟]一起研究比例的相关问题,并给出了一个求比例的函数scale的实现。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 基于dlib的人脸68特征点提取(眨眼张嘴检测)python版本
    文章目录引言开发环境和库流程设计张嘴和闭眼的检测引言(1)利用Dlib官方训练好的模型“shape_predictor_68_face_landmarks.dat”进行68个点标定 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • 本文介绍了200个经典c语言源代码,包括函数的使用,如sqrt函数、clanguagefunct等。这些源代码可以帮助读者更好地理解c语言的编程方法,并提供了实际应用的示例。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • 花瓣|目标值_Compose 动画边学边做夏日彩虹
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Compose动画边学边做-夏日彩虹相关的知识,希望对你有一定的参考价值。引言Comp ... [详细]
  • 基于移动平台的会展导游系统APP设计与实现的技术介绍与需求分析
    本文介绍了基于移动平台的会展导游系统APP的设计与实现过程。首先,对会展经济和移动互联网的概念进行了简要介绍,并阐述了将会展引入移动互联网的意义。接着,对基础技术进行了介绍,包括百度云开发环境、安卓系统和近场通讯技术。然后,进行了用户需求分析和系统需求分析,并提出了系统界面运行流畅和第三方授权等需求。最后,对系统的概要设计进行了详细阐述,包括系统前端设计和交互与原型设计。本文对基于移动平台的会展导游系统APP的设计与实现提供了技术支持和需求分析。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 本文介绍了关于Java异常的八大常见问题,包括异常管理的最佳做法、在try块中定义的变量不能用于catch或finally的原因以及为什么Double.parseDouble(null)和Integer.parseInt(null)会抛出不同的异常。同时指出这些问题是由于不同的开发人员开发所导致的,不值得过多思考。 ... [详细]
  • 概述H.323是由ITU制定的通信控制协议,用于在分组交换网中提供多媒体业务。呼叫控制是其中的重要组成部分,它可用来建立点到点的媒体会话和多点间媒体会议 ... [详细]
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社区 版权所有