[实战] 图片转素描图

本文大约 2000 字,阅读大约需要 6 分钟

我们知道图片除了最普通的彩色图,还有很多类型,比如素描、卡通、黑白等等,今天就介绍如何使用 Python 和 Opencv 来实现图片变素描图。

主要参考这篇文章来实现–How to create a beautiful pencil sketch effect with OpenCV and Python

简介

如何将图片转换成素描图呢,只需要下面四个步骤即可:

  1. 首先将彩色图转换成灰度图;
  2. 对灰度图进行求其反色的操作;
  3. 对第2步得到的结果采用一个高斯模糊的操作;
  4. 采用颜色亮化(color dodge)的技术将第一步的灰度图和第三步操作后的图片进行混合。

事先准备,首先是安装好 opencv,可以直接通过 pip 进行安装:

pip install opencv-python

接着准备一张图片,最好是颜色鲜明一点的图片,方便对比转换的效果。

原图

第一步:彩色图变灰度图

第一步变成灰度图,其实非常简单,直接调用 opencv 的函数即可,如下面代码所示:

import cv2img_rgb = cv2.imread('example.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)

图片转换效果如下所示:

图片转灰度图

上面的代码是读取图片后,再通过调用cv2.cvtColor函数将图片转换成灰度图,实际上我们可以直接在读取图片时候就直接转换图片,即:

img_gray = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)

这里调用cv2.imread函数时,设置了cv2.IMREAD_GRAYSCALE的标志,表示加载灰度图。在imread函数中是设置了三种标志,分别是

  • cv2.IMREAD_COLOR : 默认使用该种标识。加载一张彩色图片,忽视它的透明度。
  • cv2.IMREAD_GRAYSCALE : 加载一张灰度图。
  • cv2.IMREAD_UNCHANGED : 加载图像,包括它的Alpha 通道(Alpha 表示图片的透明度)。

另外,如果觉得以上标志太长,可以简单使用 1,0,-1 代替,效果是相同的。

第二步:灰度图进行反色操作

第二步就是对灰度图进行反色操作,其实就是非常简单的采用灰度图的最大像素值 255 减去当前像素值即可(因为灰度图的范围是[0, 255]),代码如下所示:

img_gray_inv = 255 - img_gray

结果如下所示:

灰度图反色

其实就是原本比较暗的地方变光亮了,而比较亮的地方变暗了。

第三步:高斯模糊

高斯模糊操作是一个有效减少图片噪音以及对图片进行平滑操作的方法,在数学上等价于对图像采用高斯核进行卷积的操作。我们可以直接调用cv2.GaussianBlur来实现高斯模糊操作,这里需要设置参数ksize,表示高斯核的大小,sigmaXsigmaY分别表示高斯核在 X 和 Y 方向上的标准差。

img_blur = cv2.GaussianBlur(img_gray_inv, ksize=(21, 21),sigmaX=0, sigmaY=0)

效果如下所示,右边图是进行高斯模糊后的结果,是有了一定的模糊效果。

高斯模糊

第四步:混合操作

第四步,就是见证奇迹的时刻!这一步骤自然就是需要得到最终的素描图结果了。在传统照相技术中,当需要对图片某个区域变得更亮或者变暗,可以通过控制它的曝光时间,这里就用到亮化(Dodging)和暗化(burning)的技术。

在现代图像编辑工具,比如 PS 可以实现上述说的两种技术。比如对于颜色亮化技术,给定一张图片 A 和 蒙版 B,那么实现做法如下所示:

(B[idx] == 255)?B[idx]:min(255, ((A[idx] << 8) / (255-B[idx])))

通过 python 代码实现上述公式,那么原始代码如下所示:

import cv2
import numpy as npdef dodgeNaive(image, mask):# determine the shape of the input imagewidth, height = image.shape[:2]# prepare output argument with same size as imageblend = np.zeros((width, height), np.uint8)for col in range(width):for row in range(height):# do for every pixelif mask[col, row] == 255:# avoid division by zeroblend[col, row] = 255else:# shift image pixel value by 8 bits# divide by the inverse of the masktmp = (image[col, row] << 8) / (255 - mask)# make sure resulting value stays within boundsif tmp > 255:tmp = 255blend[col, row] = tmpreturn blend

上述代码虽然实现了这个功能,但是很明显会非常耗时,中间采用了一个两层循环,计算复杂度是 O(w*h) ,也就是如果图片的宽和高的乘积越大,耗时就越长,所以就有了升级版的代码版本:

def dodgeV2(image, mask):return cv2.divide(image, 255 - mask, scale=256)

运行上述代码,得到的最终结果如下所示:

素描图

效果看起来还可以,除了右下角部分对于原图中黑色区域处理得不是很好。

而另一种技术—暗化操作的代码如下所示:

def burnV2(image, mask):return 255 - cv2.divide(255 - image, 255 - mask, scale=256)

效果如下图所示:

burning

完整版代码如下所示:

import cv2
import numpy as npdef dodgeNaive(image, mask):# determine the shape of the input imagewidth, height = image.shape[:2]# prepare output argument with same size as imageblend = np.zeros((width, height), np.uint8)for col in range(width):for row in range(height):# do for every pixelif mask[col, row] == 255:# avoid division by zeroblend[col, row] = 255else:# shift image pixel value by 8 bits# divide by the inverse of the masktmp = (image[col, row] << 8) / (255 - mask)# print('tmp={}'.format(tmp.shape))# make sure resulting value stays within boundsif tmp.any() > 255:tmp = 255blend[col, row] = tmpreturn blenddef dodgeV2(image, mask):return cv2.divide(image, 255 - mask, scale=256)def burnV2(image, mask):return 255 - cv2.divide(255 - image, 255 - mask, scale=256)def rgb_to_sketch(src_image_name, dst_image_name):img_rgb = cv2.imread(src_image_name)img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)# 读取图片时直接转换操作# img_gray = cv2.imread('example.jpg', cv2.IMREAD_GRAYSCALE)img_gray_inv = 255 - img_grayimg_blur = cv2.GaussianBlur(img_gray_inv, ksize=(21, 21),sigmaX=0, sigmaY=0)img_blend = dodgeV2(img_gray, img_blur)cv2.imshow('original', img_rgb)cv2.imshow('gray', img_gray)cv2.imshow('gray_inv', img_gray_inv)cv2.imshow('gray_blur', img_blur)cv2.imshow("pencil sketch", img_blend)cv2.waitKey(0)cv2.destroyAllWindows()cv2.imwrite(dst_image_name, img_blend)if __name__ == '__main__':src_image_name = 'example.jpg'dst_image_name = 'sketch_example.jpg'rgb_to_sketch(src_image_name, dst_image_name)

最后,还有一种更加快速的实现,代码如下所示,仅需四行代码即可实现转换成素描图的效果。

def rgb_to_sketch_v2(src_image_name):img_gray = cv2.imread(src_image_name, 0)img_blur = cv2.GaussianBlur(img_gray, (21, 21), 0, 0)img_blend = cv2.divide(img_gray, img_blur, scale=256)img_result = cv2.cvtColor(img_blend, cv2.COLOR_GRAY2BGR)

最后用本人比较喜欢的一个女演员的照片来看看这个转换的效果:

效果还是挺不错的!

以上就是本文的主要内容和总结,欢迎关注我的微信公众号–机器学习与计算机视觉或者扫描下方的二维码,和我分享你的建议和看法,指正文章中可能存在的错误,大家一起交流,学习和进步!

我的公众号二维码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/408882.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[实战]制作简单的公众号二维码关注图

本文大约 1933 字&#xff0c;阅读大约需要 6 分钟 最近刚刚更换了公众号名字&#xff0c;然后自然就需要更换下文章末尾的二维码关注图&#xff0c;但是之前是通过 windows 自带的画图软件做的&#xff0c;但是之前弄的时候其实还是比较麻烦的&#xff0c;所以我就想作为一名程…

linux初始化进程ppid号,linux基础(十一)--系统初始化的简谈

我们在深入学习linux之前呢首先要了解其的引导加载过程&#xff0c;这样我们就可以在判断一些在系统初始化过程的出现问题的来源&#xff0c;并及时做出处理。这个过程大概分为【开机】——【BIOS】(CMOS)——【grub或者其他引导程序】——【kernel boot】(initrd文件)——【in…

Vim 快速入门

本文大约 5000 字&#xff0c; 阅读大约需要 10 分钟在 Linux 下最常使用的文本编辑器就是 vi 或者 vim 了&#xff0c;如果能很好掌握这个编辑器&#xff0c;非常有利于我们更好的在 Linux 下面进行编程开发。vim 和 viVim是从 vi 发展出来的一个文本编辑器。代码补完、编译及…

(转载)Qt中使用cout输出的方法

&#xff08;转载&#xff09;http://blog.sina.com.cn/s/blog_4f183d960100sdxf.html最近用QT写一个控制台程序&#xff0c;却不能将提示文本输出到屏幕。 cout<<"abcd"正常运行但是屏幕上却没有输出。 解决办法&#xff1a; 在qt的工程文件(.pro文件)中加入以…

Vim快速入门

本文大约 5000 字&#xff0c; 阅读大约需要 10 分钟 在 Linux 下最常使用的文本编辑器就是 vi 或者 vim 了&#xff0c;如果能很好掌握这个编辑器&#xff0c;非常有利于我们更好的在 Linux 下面进行编程开发。 vim 和 vi Vim是从 vi 发展出来的一个文本编辑器。代码补完、编…

linux内核 cpu_die,Linux内核Crash分析

结合上面的知识&#xff0c;看下当内核打印堆栈信息时&#xff0c;都打印了上面信息。下面的打印信息是工作中遇到的一种情况&#xff0c;打印了内核的堆栈信息&#xff0c;PC指针在dev_get_by_flags中&#xff0c;不能访问的内核虚地址为45685516&#xff0c;内核中一般可访问…

[GAN学习系列] 初识GAN

本文大约 3800 字&#xff0c;阅读大约需要 8 分钟 要说最近几年在深度学习领域最火的莫过于生成对抗网络&#xff0c;即 Generative Adversarial Networks(GANs)了。它是 Ian Goodfellow 在 2014 年发表的&#xff0c;也是这四年来出现的各种 GAN 的变种的开山鼻祖了&#xff…

[GAN学习系列] 初始GAN

本文大约 3800 字&#xff0c;阅读大约需要 8 分钟要说最近几年在深度学习领域最火的莫过于生成对抗网络&#xff0c;即 Generative Adversarial Networks(GANs)了。它是 Ian Goodfellow 在 2014 年发表的&#xff0c;也是这四年来出现的各种 GAN 的变种的开山鼻祖了&#xff0…

[资源分享] 吴恩达最新《机器学习训练秘籍》中文版可以免费下载了

本文大约 600 字&#xff0c; 阅读大约需要 2 分钟 吴恩达老师在上个月底宣布终于完成了他最新的书籍《Machine Learning Yearning》的最后几个章节&#xff1a; 而最近这本书也有了免费的完整中文版下载了&#xff0c;中文版的名称是《机器学习训练秘籍》&#xff0c;封面如下…

Android学习笔记44:JSON数据解析

JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;采用完全独立于语言的文本格式&#xff0c;为Web应用开发提供了一种理想的数据交换格式。 本文将主要介绍在Android开发中&#xff0c;如何在服务器端创建JSON数据&#xff0c;以…

[GAN学习系列2] GAN的起源

本文大约 5000 字&#xff0c;阅读大约需要 10 分钟 这是 GAN 学习系列的第二篇文章&#xff0c;这篇文章将开始介绍 GAN 的起源之作&#xff0c;鼻祖&#xff0c;也就是 Ian Goodfellow 在 2014 年发表在 ICLR 的论文–Generative Adversarial Networks”&#xff0c;当然由于…

[资源分享] Github上八千Star的深度学习500问教程

本文大约 600 字&#xff0c;阅读大约需要 2 分钟这周要分享的一个资源是来自 Github 上的已经有八千多 Star 的一个深度学习知识总结&#xff0c;如下图所示&#xff1a;其 Github 地址为&#xff1a;https://github.com/scutan90/DeepLearning-500-questions它目前是有 16 个…