图片转为漫画
翻译链接图片漫画化
第一步:导入需要的库
导入OpenCV库用于图片处理
导入easygui
导入numpy 图片存储和处理为数字
导入imageio
导入matplotlib
OS 操作系统交互,这里主要时读取和存储图片路径
import cv2 #用于图像处理
import easygui #
import numpy
import imageio
import sys
import matplotlib.pyplot as plt
import os
import tkinter as tk
from tkinter import filedialog
from tkinter import *
from PIL import ImageTk, Image
第二步 创建一个文件盒用于选择指定文件
构建应用程序的主窗口,按钮、标签和图像将驻留在该窗口中。
top = tk.Tk()
# 窗口大小
top.geometry('400x400')
# 窗口名字
top.title('Cartoonify your Image')
# 窗口背景色
top.configure(background='white')
""" fileopenbox opens the box to choose file
and help us store file path as string """
def upload():
ImagePath=easygui.fileopenbox()
cartoonify(ImagePath)
第三步 读取图片
用imreadj将图像读入存为numpy, 颜色顺序是BGR,用cvtColor转换为RGB
# 读取图片
originalmage = cv2.imread(ImagePath)
originalmage = cv2.cvtColor(originalmage, cv2.COLOR_BGR2RGB)
#print(image) # 图片以数字形式存储
# 确认选中了图片
if originalmage is None:
print("Can not find any image. Choose appropriate file")
sys.exit()
# 第一个状态图,最后输出的3行2列图每个图的大小统一设置为960*540
ReSized1 = cv2.resize(originalmage, (960, 540))
#plt.imshow(ReSized1, cmap='gray')
第四步 转为灰度图
漫画效果有两个特色:1、显著的边缘 2、平滑的颜色
为了将图片转为漫画,需要做多种转换。首先,转化成灰度图,灰度图更平滑;然后我们获取图片中得边缘部分;最后我们形成一个彩色图并用边缘来mask它。
#转为灰度图
grayScaleImage = cv2.cvtColor(originalmage, cv2.COLOR_BGR2GRAY)
# 在第一个状态上改为灰度图
ReSized2 = cv2.resize(grayScaleImage, (960, 540))
#plt.imshow(ReSized2, cmap='gray')
平滑灰度图
图像平滑处理(归一化块滤波、高斯滤波、中值滤波、双边滤波)
中值滤波:在核范围内所有点的平均值取代中心像素点,核大小一般为基数,这里是5。
#applying median blur to smoothen an image
smoothGrayScale = cv2.medianBlur(grayScaleImage, 5)
ReSized3 = cv2.resize(smoothGrayScale, (960, 540))
#plt.imshow(ReSized3, cmap='gray')
第五步 检索图片的边缘
实现第一个漫画特征 显著的边缘
通过自适应阈值技术adaptiveThreshold()检索边缘并使他们更显著。
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)
scr:需要进行转换的灰度图,
maxValue,超过阈值设置的颜色(灰度值),
adaptiveMethod(自适应阈值算法)采用ADAPTIVE_THRESH_MEAN_C 即求局部领域块的平均值,
threshold Type 指定阈值类型为THRESH_BINARY即二进制阈值
blockSize:领域大小
C:adaptiveMethod计算出的值减去C为最终的阈值
#retrieving the edges for cartoon effect
#by using thresholding technique
getEdge = cv2.adaptiveThreshold(smoothGrayScale, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 9, 9)
ReSized4 = cv2.resize(getEdge, (960, 540))
#plt.imshow(ReSized4, cmap='gray')
第六步 准备蒙版图片
实现第二个漫画特征 平滑的颜色
将上一步得到的边缘盖在一个高亮的图片上,实现漫画化。用双边滤波器bilateralFilter去除噪声使图片在一定上更平滑。
双边滤波是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,达到保边去噪的目的,可以做边缘保,一般用高斯滤波去降噪,会较明显地模糊边缘,对于高频细节的保护效果并不明显。9 邻域大小,后面两个300 分别是sigmaColor和SigmaSpace, 用来产生西格玛效果,也就是让图像看起来很糟糕,像水彩一样,消除颜色的粗糙性。
bilateralFilter( InputArray src, OutputArray dst, int d, double sigmaColor,double sigmaSpace,int borderType = BORDER_DEFAULT );
sigmaColor:颜色空间滤波器的sigma值。数值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
sigmaSpace坐标空间中滤波器的sigma值,数值越大,越远的像素会相互影响.
#applying bilateral filter to remove noise
#and keep edge sharp as required
colorImage = cv2.bilateralFilter(originalmage, 9, 300, 300)
ReSized5 = cv2.resize(colorImage, (960, 540))
#plt.imshow(ReSized5, cmap='gray')
第七步 漫画化
结合上两步得到的图片
bitwise_and(src1, src2, dst=None, mask=None)
src1、src2:为输入图像
dst:可选输出变量
mask:图像掩膜,可选参数,为8位单通道的灰度图像,用于指定要更改的输出图像数组的元素,即输出图像像素只有mask对应位置元素不为0的部分才输出,否则该位置像素的所有通道分量都设置为0
#masking edged image with our "BEAUTIFY" image
cartoonImage = cv2.bitwise_and(colorImage, colorImage, mask=getEdge)
ReSized6 = cv2.resize(cartoonImage, (960, 540))
#plt.imshow(ReSized6, cmap='gray')
全部代码
import cv2 #用于图像处理
import easygui #
import numpy
import imageio
import sys
import matplotlib.pyplot as plt
import os
import tkinter as tk
from tkinter import filedialog
from tkinter import *
from tkinter import messagebox
from PIL import ImageTk, Image
top = tk.Tk()
# 窗口大小
top.geometry('400x400')
top.title('Cartoonify your Image')
top.configure(background='white')
label = Label(top, background='#CDCDCD', font=('arial', 20, 'bold'))
# fileopenbox 以字符串形式返回选择的路径
def upload():
ImagePath = easygui.fileopenbox()
cartoonify(ImagePath)
def cartoonify(ImagePath):
# 读取图片 imread是cv2通常用于以数字形式保存图片
originalmage = cv2.imread(ImagePath)
originalmage = cv2.cvtColor(originalmage, cv2.COLOR_BGR2RGB)
if originalmage is None:
print("Can not find any image. Choose appropriate file")
Resized1 = cv2.resize(originalmage, (960,540))
#
plt.imshow(Resized1, cmap='gray')
# 转成灰度图
# cvtColor(image,flag)是cv2中将图片转化为flag中提到的色彩空间中的颜色
grayScaleImage = cv2.cvtColor(originalmage, cv2.COLOR_BGR2GRAY)
Resized2 = cv2.resize(grayScaleImage, (960,540))
plt.imshow(Resized2, cmap='gray')
# 平滑图片用的是模糊效果,用了中值模糊medianBlur()这个函数来平滑一个图片,
smoothGrayScale = cv2.medianBlur(grayScaleImage, 5)
Resized3 = cv2.resize(smoothGrayScale,(960,540))
plt.imshow(Resized3, cmap='gray')
# 用阈值技术检索卡通效果的边缘
getEdge = cv2.adaptiveThreshold(smoothGrayScale,255,cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 9, 9)
Resized4 =cv2.resize(getEdge, (960,540))
plt.imshow(Resized4, cmap='gray')
# 用一个双边滤波器(bilateraFilter())去除噪声保持边缘锐度
colorImage =cv2.bilateralFilter(originalmage, 9, 300, 300)
Resized5 = cv2.resize(colorImage, (960,540))
plt.imshow(Resized5, cmap='gray')
cartoonImage = cv2.bitwise_and(colorImage, colorImage, mask=getEdge)
Resized6 = cv2.resize(cartoonImage, (960,540))
plt.imshow(Resized6, cmap='gray')
plt.show()
images = [Resized1, Resized2, Resized3, Resized4, Resized5, Resized6]
fig, axes = plt.subplots(3,2, figsize=(8,8), subplot_kw={'xticks':[], 'yticks':[]},
gridspec_kw=dict(hspace=0.1, wspace=0.1))
for i, ax in enumerate(axes.flat):
ax.imshow(images[i], cmap='gray')
save1 = Button(top, text='save cartoon image', command=lambda: save(Resized6, ImagePath),
padx=30, pady=5)
save1.configure(background='#364156', foreground='white', font=('arial', 10, 'bold'))
# 放置位置
save1.pack(side=TOP, pady=50)
plt.show()
def save(Resized6, ImagePath):
# 用imwrite存储图片
newName = "cartoonified_Image"
path1 = os.path.dirname(ImagePath)
extension = os.path.splitext(ImagePath)[1]
path = os.path.join(path1, newName + extension)
cv2.imwrite(path, cv2.cvtColor(Resized6, cv2.COLOR_RGB2BGR))
I = "Image saved by name " + newName + " at " + path
tk.messagebox.showinfo(title=None, message=I)
# 创建按钮
upload = Button(top, text='Cartoonify an image', command=upload, padx=10, pady=5)
# 背景色、前景色、字体配置
upload.configure(background='#364156', foreground='white', font=('arial',10,'bold'))
upload.pack(side=TOP,pady=50)
top.mainloop()