NO.2 机器视觉 几何变化 & 特效处理
程序员文章站
2022-03-25 17:57:15
...
零蚀
几何操作
-
图像的放大缩小
-
图像的放大&缩小,是将 图片变大变小,由于图像的宽高发生了变化,所以我们需要重新计算每个位置的图像像素的值。
-
图像常见的缩放算法有以下4种
- 最近领域插值法
- 双线插值
- 像素关系重采样
- 立方插值
-
最近领域插值,是将图像扩大后的空白像素点,由附近的像素颜色进行计算填充。
-
手动实现缩放,在缩小时候 ,以下方法会有像素丢失,放大也会导致像素严重失真,因为会获取到临近点的像素值,(这也是最近领域插值)。
import cv2 as cv import numpy as np if __name__ == '__main__': src = cv.imread("../source/android.png") height = src.shape[0] width = src.shape[1] # 缩放的系数 scale = 2 # 构建行的mat n_height = int(height * scale) n_width = int(width * scale) dst = np.zeros((n_height, n_width, 3), np.uint8) for row in range(n_height): for col in range(n_width): # 找到对应在原图的像素位置的像素点 current_row_value = int(row / scale) current_col_value = int(col / scale) dst[row, col] = src[current_row_value, current_col_value] cv.imshow("", src) cv.imshow("", dst) cv.waitKey(0)
- 双线插值,如下图所示,他是计算出这个小数点对应的原图的坐标位置然后进行色值权重计算,得出新的色值,比如(1.2 ,2.3)如果是最近领域插值法,就会直接进行直接的取整,就是(1 ,2)的像素点坐标,但是这种方式,在色彩波动较大的区域会导致取值很不友好,所以双线插值根据四周的情况进行一个取值计算,这就像根据坐标算出卷积核,然后再用卷积算出当前点的色值。
-
-
根据矩阵来操作图像
- 基本的图像操作会矩阵如下图所示操作,根具与我们的每个像素点的操作来计算出变化后的位置
缩 放 = [ k x 0 0 0 k y 0 0 0 1 ] 旋 转 = [ c o s θ − s i n θ 0 s i n θ c o s θ 0 0 0 1 ] 位 移 = [ 1 0 t x 0 1 t y 0 0 1 ] 缩放= \begin{bmatrix}k_x & 0 & 0 \\ 0 & k_y & 0\\ 0 & 0 & 1\end{bmatrix} 旋转=\begin{bmatrix}cos\theta & -sin\theta & 0 \\ sin\theta & cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} 位移 = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1\end{bmatrix} 缩放=⎣⎡kx000ky0001⎦⎤旋转=⎣⎡cosθsinθ0−sinθcosθ0001⎦⎤位移=⎣⎡100010txty1⎦⎤
# 坐标(为了满足矩阵相乘而补的齐次矩阵,最后一个参数也可以作为平移的单位的倍数) matrixA = np.array([x, y, 1]) # 平移矩阵(相当于x向右平移200,y向右平移100) matrixB = np.array([[1, 0, 200], [0, 1, 100]]) # 平移矩阵 * 坐标矩阵的转秩 print(matrixB.dot(matrixA.T))
- 手动实现图像的平移
import cv2 as cv import numpy as np if __name__ == '__main__': src = cv.imread("../source/android.png") height = src.shape[0] width = src.shape[1] dst = np.zeros(src.shape, np.uint8) x = 3 y = 2 for row in range(height): for col in range(width): matrix_src = np.array([col, row, 1]) matrix_tool = np.array([[1, 0, 100], [0, 1, 50]]) # 得到新的像素点位置 result = matrix_tool.dot(matrix_src) new_col = result[0] new_row = result[1] if new_row < height and new_col < width: dst[new_row, new_col] = src[row, col] cv.imshow("1", src) cv.imshow("2", dst) cv.waitKey(0)
- 上述原理实现平移的过程,opencv有API,
import cv2 as cv import numpy as np import math src = cv.imread("../source/android.png") height = src.shape[0] width = src.shape[1] # 平移以前版本不支持np.array matrixTranslate = np.float32([[1, 0, 100], [0, 1, 50]]) # 仿射交换(matrixTranslate 必须是2行3列) dst = cv.warpAffine(src, matrixTranslate, (width, height)) cv.imshow("1", src) cv.imshow("2", dst) cv.waitKey(0)
- 旋转图像的API,如果设置旋转中心(0,0)的话,坐标系和android的坐标系是一致的(左上角原点向右下为正方向)
""" :param center 旋转中心 :param angle 角度 :param scale 缩放 """ matrixTranslate = cv.getRotationMatrix2D((width / 2, height / 2), 45, 0.8) dst = cv.warpAffine(src, matrixTranslate, (width, height))
- 矩阵变换:我们可以根据矩阵的三个点的坐标变化来进行拉伸变化,但是图像信息并不会丢失,如下图所示。(需要注意的是这里只是改变3个坐标点,)
# 初始矩阵(四点坐标列表) matrix_origin = np.float32([[0, 0], [0, height], [width, 0]]) # 目标矩阵 matrix_aim = np.float32([[0, 50], [0, height - 50], [width, 0]]) # 变化矩阵 matrix_change = cv.getAffineTransform(matrix_origin, matrix_aim)
-
金字塔
- 图像分析时候,缩小图像如果使用cv.resize(mat,(w,h))图像信息会像素失真严重,所以为了保证图像轮廓保持清晰,我们会使用下采样的方式,图像金字塔中的上采样和下采样不会使得图像的像素轮廓信息丢失严重,它的内部是依赖于高斯模糊来实现。
import cv2 as cv src = cv.imread("../source/android.png") cv.imshow("1",src) # 上采样 #dst=cv.pyrUp(src) # 下采样(每次下采样,会将分辨率低1/2) dst=cv.pyrDown(src) cv.imshow("2",dst) cv.waitKey()
- 图像融合:和安卓中描述的权重叠加方式一样,就没有什么好说的了。想了解可以看一下,原理和API都一样。[???? NO.2 Android Opencv 像素处理]
import cv2 as cv src1 = cv.imread("../source/android.png") src2 = cv.imread("../source/clothes.png") # 两个图像融合(信息重叠)需要图像长,宽相同 dst = cv.addWeighted(src1, 0.5, src2, 0.5, 0) cv.imshow("1", src1) cv.imshow("2", src2) cv.imshow("3", dst) cv.waitKey()
特效处理
-
灰度图像处理
- 单通道处理,这里首先是将一个图像的BGR值进行剥离,分别用三个图像来展示这个图像中的BGR的单个通道值。
import cv2 as cv src = cv.imread('../source/android.png') # 分离的每个channel的数值,分别是BGR result = cv.split(src) cv.imshow("blue", result[0]) cv.imshow("green", result[1]) cv.imshow("red", result[2]) cv.waitKey(0)
- 如果我们想吧一个图像变成灰度图,可以有几种方法:一种是我们在读取的时候直接以灰度图的方式进行读取。
# 读取的时候直接以灰度图读取 src = cv.imread('../source/android.png', cv.IMREAD_GRAYSCALE) # cvtColor方式,和Android 一致 dst = cv.cvtColor(src, cv.COLOR_BGRA2GRAY)
-
颜色取反&浮雕&马赛克
- 取反,与运算,或运算,与或运算,这里所有的操作都是对像素的操作,他的操作如下所示,要注意的是,我们这些操作的图片的大小一定要相同,不然会报错。
import cv2 as cv import numpy as np src = cv.imread('../source/android.png') src1 = cv.imread('../source/clothes.png') dst1 = np.zeros(src.shape, src.dtype) dst2 = np.zeros(src.shape, src.dtype) dst3 = np.zeros(src.shape, src.dtype) dst4 = np.zeros(src.shape, src.dtype) # 1取反 cv.bitwise_not(src, dst1) # 2与运算 cv.bitwise_and(src, src1, dst2) # 3或运算 cv.bitwise_or(src, src1, dst3) # 4与或运算 cv.bitwise_xor(src, src1, dst4) cv.imshow("1", dst1) cv.imshow("2", dst2) cv.imshow("3", dst3) cv.imshow("4", dst4) cv.waitKey(0)
- 均值模糊代码,通过卷积的相同得权重从而获取的模糊效果。Android**???? NO.3 Android Opencv 图像处理**中已经对每个模糊手段的原理和方式进行解答,这里不再赘述,感兴趣可以看看原理和公式。
src = cv.imread('../source/android.png') dst = np.zeros(src.shape, src.dtype) """ // 均值模糊 :param src 源数据 :param ksize 卷积和的范围 :param dst 输出数据 :param point 位置默认是(-1,-1) :param type 边缘类型 """ cv.blur(src, (15, 1), dst, (-1, -1), cv.BORDER_DEFAULT) cv.imshow("1", dst) cv.waitKey()
- 马塞克的原理,马赛克的原理,就是将某个像素点的额色值复制给周边像素,导致颜色区域变大,最后轮廓失真导致马赛克,那么我们手写一个马赛克。
src = cv.imread('../source/android.png') dst = np.zeros(src.shape, src.dtype) height = src.shape[0] width = src.shape[1] maxLine = 100 color = src[0, 0] # 马赛克范围大小 offset = 10 for rows in range(height): for cols in range(width): if rows < maxLine: if rows % offset == 0 and cols % offset == 0: color = src[rows, cols] # 将 10 * 10 的区域同色化 for i in range(offset): for j in range(offset): src[rows + i, cols + j] = color cv.imshow("1", src) cv.waitKey()
- 浮雕效果,模糊有些事让边缘处于一种不清晰状态,而浮雕效果则是让轮廓更加明显。这就类似最简单的边缘检测,通过梯度方式(一阶导数),如果变化平坦就舍弃,如果有边界就保留。(一下就是当前像素减去前一个像素,差值的绝对值为当前像素值)
src = cv.imread("../source/android.png") gray = cv.cvtColor(src, cv.COLOR_BGRA2GRAY) dst = np.zeros(src.shape, src.dtype) height = src.shape[0] width = src.shape[1] for row in range(height): for cols in range(1, width): # 获取前一个灰度值 gray1 = gray[row, cols - 1] # 获取当前的灰度值 gray2 = gray[row, cols] # 因为是uint8格式,如果是负数,会高位填充导致数据错误 # 获取梯度值 gradient = abs(int(gray2) - int(gray1)) dst[row, cols] = gradient cv.imshow("1",dst) cv.waitKey()
-
形状绘制
- 绘制形状,这里的绘制形状和android有些不同,Android里如果想绘制出来的形状有颜色必须要将BRGA的类型转变为BGR才可以进行颜色绘制。而python中不需要计较这些。(参数参考源码)
import cv2 as cv import numpy as np src = cv.imread("../source/android.png") src1 = cv.imread("../source/android.png") src2 = cv.imread("../source/android.png") # 画线 cv.line(src, (0, 0), (200, 200), (255, 255, 0), 8) # 画圆 cv.circle(src1, (100, 100), 50, (100, 100, 0), 8) # 画矩形 cv.rectangle(src2, (50, 50), (150, 150), (0, 0, 255), 8) cv.imshow("1", src) cv.imshow("2", src1) cv.imshow("3", src2) cv.waitKey()
- 绘制文字,
""" 绘制文本(不支持中文) org 位置 fontface 样式 fontscale 缩放 """ cv.putText(src4, "this is text", (0, 50), cv.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2)
-
滑动进度条的使用。
src = cv.imread("../source/android.png") def change_offset(offset): height = src.shape[0] width = src.shape[1] for i in range(height): for j in range(width): B = offset if offset < 255 else 255 src[i, j][0] = B cv.imshow("dst", src) print(offset) """ 创建滑动条 ⚠️:进度条要在imshow之后创建,因为他要根据名称来确定添加的窗口 :param name 滑动条的名称(滑动条前面的的文字label) :param windowname 需要定位的窗口名称 :param value 起始值 :param count 数值长度 :param onChange 回调函数 (fun(value)形式) """ change_offset(0) cv.createTrackbar("process", "dst", 0, 255, change_offset)
上一篇: C# enum
下一篇: HTML连载61-焦点图、固定定位