如何使用Python(NumPy和OpenCV)对图像进行 Funkify
作者:互联网
工作方式
代码的主要功能将图像作为输入,并将funkified图像作为输出返回。主要功能如下所示:
def funkify(self,img): edges = self.edge_mask(img) 模糊 = cv2。GaussianBlur(img,(self.color_blur_val,self.color_blur_val), sigmaX=0,sigmaY=0) 指数 = self.pick_color(blur.reshape((-1, 3)), self.lightness,self.n_colors) 重新着色 = np.uint8(self.colors[indices].reshape(blur.shape)) 卡通= cv2.bitwise_and(重新着色,重新着色,面具=边缘) 返回卡通
现在,我将以这张图片为例,逐行细分。
第1步:thiccc边缘
这个项目基于这个博客。我想重新使用代码,以便在网络摄像头上实时运行。事实证明,用他们的方法是不可能的,但他们的一些代码仍然非常好。特别是找到图像中边缘并使其变厚的功能。
加厚边缘的目的是让它看起来像卡通或动漫中的墨水线。获取边缘由代码中的第一行完成。
edges = self.edge_mask(img)
edge_mask()函数调用以下代码。
def edge_mask(self,img): # 获取图像的边缘 灰色 = cv2.cvtColor(img,cv2。颜色_BGR2灰色) gray_blur = cv2。GaussianBlur(灰色, (self.edge_blur_val,self.edge_blur_val), -1) edges = cv2.adaptiveThreshold(gray_blur,255, cv2。ADAPTIVE_THRESH_MEAN_C, cv2。THRESH_BINARY, self.block_size, 2) 返回边缘
前两行是预处理图像,为自适应阈值函数做准备。图像从颜色转换为灰度,然后使用高斯内核进行模糊,因此自适应阈值函数将产生更少的噪声输出。
第三行是主要部分。自适应阈值是一种二进制阈值函数。这意味着它将图像中的每个像素归类为黑色(0)或白色(1)。有许多二进制阈值算法。自适应阈值的独特之处在于,它根据相邻像素的强度而不是整个图像对每个像素进行分类。这使得它在不同的照明条件下表现得更好。
使用此功能,可以快速找到图像的边缘,这使得它非常适合我们实时运行的版本。这是应用于我们示例图像的这个函数。
第2步:重新着色图像
下一步是重新着色图像。使某些东西看起来卡通化的原因之一是图像中的颜色较少。例如,由于照明,真实图像将有数千种不同的颜色和色调,但卡通只有几种颜色。这可以使用卡通着色器来完成,这在电子游戏中很常见。
在我基于这个项目的代码中,他们使用K-均值将像素聚类为一定数量的颜色。然后,每个像素都更改为其组的平均颜色。虽然这效果很好,但它太慢了,无法实时使用。此外,这有点无聊。
相反,颜色是手动选择的,然后所有像素都根据它们在亮度上最接近的颜色被覆盖为这些颜色。这消除了寻找颜色的需要,老实说,它看起来就像你想要的一样酷。选择亮度——对亮度的定量测量)——是因为它保留了原始图像的照明效果。
主函数中重新着色的代码由以下三行组成:
模糊 = cv2。GaussianBlur(img,(self.color_blur_val,self.color_blur_val), sigmaX=0,sigmaY=0) 指数 = self.pick_color(blur.reshape((-1, 3)), self.luminance,self.n_colors) 重新着色 = np.uint8(self.colors[indices].reshape(blur.shape))
第一行只是模糊图像,就像边缘变模糊一样。这样做的目的是使最终图像中的颜色看起来更光滑。
第二行是最重要的。pick_color()函数是计算图像中每个像素的颜色。它运行以下代码:
def pick_color(self,img,color_lums,n_colors): #根据亮度重新分配像素颜色 # 获取像素的亮度 lum_mult = [0.114, 0.587, 0.299] img_lum = np.sum(np.multiply(img, lum_mult), axis=1) #为每种颜色创建条件列表 condlist = [] 选择列表 = [] 对于范围(n_colors)的i: choicelist.append(i) 如果我<n_colors-1: condlist.append(img_lum < (color_lums[i]+color_lums[i+1])/2) 其他: condlist.append(img_lum > (color_lums[i]+color_lums[i-1])/2) #获取每个像素的新颜色索引 inds = np.select(condlist,choicelist) 返回ins
首先,该函数使用此堆栈溢出帖子的公式从RGB值中计算每个像素的亮度。
然后,创建一个条件列表,用于根据亮度确定选择哪种颜色。制作此条件列表允许该功能适应用户想要使用的任何数量的颜色,而不是硬编码一组数字。np.select()函数用于实际从该列表中进行选择。它本质上是每个像素的一系列“if语句”,像这样:
#例如3种颜色 如果 pix_lum < (color_lums[0] + color_lums[1])/2: pix_ind = 0 else if pix_lum < (color_lums[1] + color_lums[2])/2: pix_ind = 1 其他: pix_ind = 2
第三行根据我们从thepick_color()函数获得的索引重新着色图像。它只需通过切片图像数组,然后将其转换为uint_8来做到这一点,以便可以正确显示。
这个过程的输出看起来像这样。
第3步:合并
主要功能的最后一部分是将重新着色的图像和厚边缘结合起来。
卡通= cv2.bitwise_and(重新着色,重新着色,面具=边缘)