封面
需要照片编辑软件都提供了一些特效,例如水彩特效、油画特效以及卡通特效。今天介绍就是如何使用 opencv 来实卡通特效。随后会更新更多相关内容,并且更新此文章。先把代码给大家分享一下。
import argparse
import time
import numpy as np
from collections import defaultdict
from scipy import stats
import cv2
原图
def cartoonize(image):
output = np.array(image)
x, y, c = output.shape
for i in range(c):
print(i)
output[:, :, i] = cv2.bilateralFilter(output[:, :, i], 5, 50, 50)
edge = cv2.Canny(output, 100, 200)
output = cv2.cvtColor(output, cv2.COLOR_RGB2HSV)
hists = []
#H
hist, _ = np.histogram(output[:, :, 0], bins=np.arange(180+1))
hists.append(hist)
#S
hist, _ = np.histogram(output[:, :, 1], bins=np.arange(256+1))
hists.append(hist)
#V
hist, _ = np.histogram(output[:, :, 2], bins=np.arange(256+1))
hists.append(hist)
C = []
for h in hists:
C.append(k_histogram(h))
print("centroids: {0}".format(C))
output = output.reshape((-1, c))
for i in range(c):
channel = output[:, i]
index = np.argmin(np.abs(channel[:, np.newaxis] - C[i]), axis=1)
output[:, i] = C[i][index]
output = output.reshape((x, y, c))
output = cv2.cvtColor(output, cv2.COLOR_HSV2RGB)
contours, _ = cv2.findContours(edge,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_NONE)
# for i in range(len(contours)):
# tmp = contours[i]
# contours[i] = cv2.approxPolyDP(tmp, 2, False)
cv2.drawContours(output, contours, -1, 0, thickness=1)
return output
- bilateralFilter 对图像进行双边滤波处理,对图像进行模糊处理
def main():
img = cv2.imread("images/girl-face.jpg",1)
print img.shape
img = cv2.resize(img,(310,552))
img = np.array(img)
x, y, c = img.shape
for i in range(c):
img[:, :, i] = cv2.bilateralFilter(img[:, :, i], 5, 50, 50)
# break
while True:
cv2.imshow("image",img)
key = cv2.waitKey(1)
if key == 27:
break
if __name__ == "__main__":
main()
图
- Canny 用于进行边缘检测
def main():
img = cv2.imread("images/girl-face.jpg",1)
img = cv2.resize(img,(310,552))
img = np.array(img)
x, y, c = img.shape
for i in range(c):
img[:, :, i] = cv2.bilateralFilter(img[:, :, i], 5, 50, 50)
edge = cv2.Canny(img, 100, 200)
contours, _ = cv2.findContours(edge,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, -1, 0, thickness=1)
# break
while True:
cv2.imshow("image",img)
key = cv2.waitKey(1)
if key == 27:
break
if __name__ == "__main__":
main()
边缘检测
def main():
img = cv2.imread("images/girl-face.jpg",1)
img = cv2.resize(img,(310,552))
img = np.array(img)
x, y, c = img.shape
for i in range(c):
img[:, :, i] = cv2.bilateralFilter(img[:, :, i], 5, 50, 50)
img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
hists = []
hist,_ = np.histogram(img[:,:,0],bins=np.arange(180+1))
print(hist)
plt.plot(hist)
plt.show()
图
def update_C(C, hist):
"""
update centroids until they don't change
"""
while True:
groups = defaultdict(list)
#assign pixel values
for i in range(len(hist)):
if hist[i] == 0:
continue
d = np.abs(C-i)
index = np.argmin(d)
groups[index].append(i)
new_C = np.array(C)
for i, indice in groups.items():
if np.sum(hist[indice]) == 0:
continue
new_C[i] = int(np.sum(indice*hist[indice])/np.sum(hist[indice]))
if np.sum(new_C-C) == 0:
break
C = new_C
return C, groups
def k_histogram(hist):
"""
choose the best K for k-means and get the centroids
"""
alpha = 0.001 # p-value threshold for normaltest
N = 80 # minimun group size for normaltest
C = np.array([128])
while True:
C, groups = update_C(C, hist)
#start increase K if possible
new_C = set() # use set to avoid same value when seperating centroid
for i, indice in groups.items():
#if there are not enough values in the group, do not seperate
if len(indice) < N:
new_C.add(C[i])
continue
# judge whether we should seperate the centroid
# by testing if the values of the group is under a
# normal distribution
z, pval = stats.normaltest(hist[indice])
if pval < alpha:
#not a normal dist, seperate
left = 0 if i == 0 else C[i-1]
right = len(hist)-1 if i == len(C)-1 else C[i+1]
delta = right-left
if delta >= 3:
c1 = (C[i]+left)/2
c2 = (C[i]+right)/2
new_C.add(c1)
new_C.add(c2)
else:
# though it is not a normal dist, we have no
# extra space to seperate
new_C.add(C[i])
else:
# normal dist, no need to seperate
new_C.add(C[i])
if len(new_C) == len(C):
break
else:
C = np.array(sorted(new_C))
return C
这里说 argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('input', help='input image')
parser.add_argument('output', help='output image')
args = parser.parse_args()
print(args.input)
print(args.output)
image = cv2.imread(args.input,1)
output = cartoonize(image)
cv2.imwrite(args.output,output)
print("hello world")
python3 cartoonize.py "images/girl-face.jpg" "output.jpg"
最终效果