图像特效

灰度处理

一般来说,彩图图像中的每个像素是由B、G、R三个颜色通道组成的,每个颜色通道占8位,于是每个像素占24位。而灰度图像中的每个像素都是灰度等级,只有一个颜色通道,占8位。

图像灰度处理的原理是:将彩色图中每个像素的三个颜色通道,根据某一公式,算出灰度图中对应像素的灰度值。

灰度处理的公式可以如下,即取三个颜色通道的均值。

$$gray = {(B+G+R) \over 3}$$

根据心理学得出如下的灰度处理公式。

$$gray = {R * 0.299 + G * 0.587 + B * 0.114}$$

OpenCV提供了便捷的API来实现图像的灰度处理。

import cv2
# 利用cv2.imread()载入图片时,参数二传入0表示载入为灰度图像
img = cv2.imread('flower.jpg', 0)
1
2
3
import cv2
# cv2载入图像的颜色空间默认为BGR
img = cv2.imread('flower.jpg', 1)
# 利用cv2.cvtColor进行颜色空间转换,cv2.COLOR_BGR2GRAY表示颜色空间从BGR转为Gray
dst = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
1
2
3
4
5

颜色反转

颜色反转就是将图像中的每个像素值的每个颜色通道值反转。

一个颜色通道占8位,取值范围为0~255。所谓的颜色通道值反转就是用255减去原颜色通道值。

灰度图的颜色反转的py代码实现如下:

for i in range(0, height):
    for j in range(0, width):
        gray = img[i, j]
        img[i, j] = 255 - gray
1
2
3
4

彩色图的颜色反转的py代码实现如下:

for i in range(0, height):
    for j in range(0, width):
        (b, g, r) =  img[i, j]
        dst[i, j] = (255-b, 255-g, 255-r)
1
2
3
4

马赛克

马赛克的原理是将指定区域所有点的像素值全部改为该区域某一个点(如左上角第一个点)的像素值。

马赛克原理

毛玻璃

毛玻璃的原理就是用当前点周围一定区域内任意一点的像素值来替换当前点像素值。


for m in range(0, height):
    for n in range(0, width):
        # 四周SCOPE区域内随机一点
        index = int(random.random() * SCOPE)
        # 避免随机点越界
        while m + index >= height or n + index >= width:
            index = int(random.random() * SCOPE)
        # 用随机点的像素值替换当前点
        (b, g, r) = img[m+index, n+index]
        dst[m, n] = (b, g, r)
1
2
3
4
5
6
7
8
9
10
11

图片融合

利用OpenCV的addWeighted函数来使两个图片加权和来实现图片融合。

void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray
dst, int dtype=-1)
1
2
  • src1:第一张图片的图像数据。

  • alpha:第一张图片的权重。

  • src2:第二张图片的图像数据。src2与src1有相同的大小和颜色通道数量。

  • beta:第二张图片的权重。

  • gamma:加权和后的像素值的偏移量。

  • dst:两个图像加权和后的输出图像。

  • dtype:dst的可选深度,默认为-1。当src1和src2具有相同的深度时,此参数可设置为-1,表示dst的深度等于src1的深度。

addWeighted函数的计算公式为:dst = src1 * alpha + src2 * beta + gamma。

一般来说,alpha + beta = 1。

addWeighted函数要求src1和src2具有相同的大小,一般需要在较大的图像(如src1)中设置感兴趣区域ROI,此ROI与较小的那个图像(如src2)尺寸一致,接着用该ROI范围内的图像数据与较小的那个图像的数据进行融合操作。

# 在src1中设置ROI:左上角为src1的(0, 0),高为src2高度,宽为src2宽度
src1ROI = src1[0:src2Height, 0:src2Width]
addWeighted(src1ROI, 0.6, src2, 0.4, 0, dst)
1
2
3

边缘检测

TODO

浮雕效果

浮雕效果的实现方法很多,下面介绍一种常见的实现步骤:

  1. 原图灰度处理。

  2. 利用如下的矩阵和灰度图进行卷积运算,再加上偏移量128,结果作为当前点的像素值。

$$ \left{ \begin{matrix} 1 & 0 & 0 \ 0 & 0 & 0 \ 0 & 0 & -1 \end{matrix} \right} $$

灰度图与上面的矩阵进行卷积运算,实质上是当前点(i,j)的左上点(i-1,j-1)减去右下点(i+1,j+1),再加上128,最后的结果值替换当前点的像素值。

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

for i in range(1, height - 1):
    for j in range(1, width - 1):
        gray0 = int(gray[i-1, j-1])
        gray1 = int(gray[i+1, j+1])
        out = gray0 - gray1 + 128
        if out > 255:
            out = 255
        if out < 0:
            out = 0
        dst[i, j] = out
1
2
3
4
5
6
7
8
9
10
11
12

颜色映射

颜色映射就是根据一个映射表或公式,将原图中的像素的颜色映射成新的颜色值。

for i in range(0, height):
    for j in range(0, width):
        (b, g, r) = img[i, j]
        b = b * 1.5
        g = g * 1.3
        if b > 255:
            b = 255
        if g > 255:
            g = 255
        dst[i, j] = (b, g, r)
1
2
3
4
5
6
7
8
9
10

油画特效

油画特效的实现步骤:

  1. 对原图进行灰度处理,得到用于分析的灰度图。

  2. 遍历原图的每个像素点(i,j),分析灰度图对应点(i,j)周围区域内的像素值情况,将分析结果作为原图的像素点(i,j)的像素值。

像素点(i,j)的分析步骤:

  1. 确定分析区域:以当前点为中心的,大小为BLOCK_SIZE的矩形。

  2. 将灰度全值256进行等级划分,如划分为8个32。

  3. 在灰度图中,统计分析区域内各像素点对应的灰度等级。

  4. 找出最高频的灰度等级的像素个数和灰度等级索引。

  5. 在彩色图中,对分析区域内所有满足最高频灰度等级的像素点的各颜色通道值分别进行累加。

  6. 分别求出上面各颜色通道均值(颜色通道值 / 最高频灰度等级的像素个数),并将其作为(i,j)的像素值的对应颜色通道值。

import cv2
import numpy as np

img = cv2.imread('../res/flower0.jpg', 1)
imgInfo = img.shape
height = imgInfo[0]
width = imgInfo[1]

ANALYSE_AREA_SIZE = 8
ANALYSE_AREA_SIZE_HALF = int(ANALYSE_AREA_SIZE / 2)
GRAY_LV_NUM = 8
GRAY_LV_EACH_VALUE = int(256 / GRAY_LV_NUM)

# 灰度处理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

dst = np.zeros((height, width, 3), np.uint8)

for i in range(-ANALYSE_AREA_SIZE_HALF, height - ANALYSE_AREA_SIZE_HALF):
    for j in range(-ANALYSE_AREA_SIZE_HALF, width - ANALYSE_AREA_SIZE_HALF):
        # 灰度等级划分
        grayLvCounter = np.zeros(GRAY_LV_NUM, np.uint32)
        # 在灰度图中,统计分析区域内各像素点对应的灰度等级
        for m in range(-ANALYSE_AREA_SIZE_HALF, ANALYSE_AREA_SIZE_HALF):
            for n in range(-ANALYSE_AREA_SIZE_HALF, ANALYSE_AREA_SIZE_HALF):
                grayValue = gray[i+m, j+n]
                grayIndex = int(grayValue / GRAY_LV_EACH_VALUE)
                grayLvCounter[grayIndex] += 1
        # 找出最高频的灰度等级的像素个数和灰度等级索引
        maxGrayLvCount = np.max(grayLvCounter)
        maxGrayLvIndex = np.argmax(grayLvCounter)
        # 在彩色图中,对分析区域内所有满足最高频灰度等级的像素点的各颜色通道值分别进行累加
        bgrSum = [0, 0, 0]
        for m in range(-ANALYSE_AREA_SIZE_HALF, ANALYSE_AREA_SIZE_HALF):
            for n in range(-ANALYSE_AREA_SIZE_HALF, ANALYSE_AREA_SIZE_HALF):
                grayIndex = int(gray[i+m, j+n] / GRAY_LV_EACH_VALUE)
                if grayIndex == maxGrayLvIndex:
                    bgrSum += img[i+m, j+n]
        # 分别求出上面各颜色通道均值(颜色通道值 / 最高频灰度等级的像素个数),并将其作为当前点的像素值的对应颜色通道值
        b = int(bgrSum[0] / maxGrayLvCount)
        g = int(bgrSum[1] / maxGrayLvCount)
        r = int(bgrSum[2] / maxGrayLvCount)
        dst[i, j] = (b, g, r)
        
cv2.imshow('src', img)
cv2.imshow('dst', dst)
cv2.waitKey(0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47