「CV」 图像处理问答
读写 | 算数运算 | 边界填充 | 轮廓检测 |
形态学 | 仿射变换 | 插值运算 | |
噪声 | |||
HOG | |||
读写
imread()
# LOAD_IMAGE_UNCHANGED (<0)读取透明通道 LOAD_IMAGE_GRAYSCALE (=0)灰度 LOAD_IMAGE_COLOR (>0)RGB
# 0代表透明,255代表不透明
算数运算
cv2.add(a, b) # 逐像素相加,会改变颜色
cv2.addWeighted(a, 0.7, b, 0.3, 0) # 按比例相加,有透明效果
# numpy 的 +,溢出后取余
cv2.seamlessClone(src, dst, np.zeros_like(src), center, cv2.NORMAL_CLONE) # 边界处透明的覆盖操作
bitwise_and、bitwise_or、bitwise_not、bitwise_xor
提取 mask + 位运算(置0) + add 可以实现 ROI 复制
边界填充
cv2.copyMakeBorder()
# borderMode: BORDER_REPLICATE 重复边界像素 BORDER_CONSTANT 填充固定值,默认 0 BORDER_ISOLATED 有效区域
# BORDER_REFLECT 反射 BORDER_REFLECT101 / BORDER_DEFAULT 对称
轮廓检测
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours()
# 每个轮廓 contours[i] 对应 4 个 hierarchy 元素 hierarchy[i][j],分别表示后前外内轮廓的索引编号,默认为负数;
# 等级关系: RETR_LIST 不要、RETR_TREE 树形、RETR_CCOMP 内外、RETR_EXTERNAL 外
# 轮廓近似
# CHAIN_APPROX_NONE 所有、CHAIN_APPROX_SIMPLE 横纵对角线只留终点
# CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用 teh-Chinl chain 近似算法?
(x_center, y_center), (w, h), rotate = cv2.minAreaRect(contours[0]) # 最小外接巨型
box = np.int0(cv2.boxPoints(rect)) #通过box会出矩形框
x_tf, y_tf, w, h = cv2.boundingRect(c) # 外接矩形
ROI 提取:轮廓提取到 ROI——绘制 ROI(白色)——与原图取交集
形态学
Mat kernel(5,5,CV_8U,cv::Scalar(1));
Mat kernel = getStructuringElement(MORPH_RECT, (5, 5))
morphologyEx(); // MORPH_OPEN、MORPH_CLOSE、MORPH_GRADIENT、MORPH_TOPHAT、MORPH_BLACKHAT
erode() // 腐蚀
dilate() // 膨胀
膨胀:扩大
腐蚀:消除
开:断开
闭:连接
应用:
- 消除噪声;
- 断开独立的图像元素,连接相邻的元素;
- 寻找图像中的明显的极大值区域或极小值区域;
同一幅图像连续使用开操作或者闭操作是没有任何意义的,操作结果都和第一次的相同
操作的像素是黑色
噪声
椒盐噪声:随机出现的黑白点;通常来自于影响信号收到强烈干扰、数位转换器或位元传输错误而产生;
高斯噪声:正态分布,钟形曲线;
仿射变换
M = cv2.getRotationMatrix2D(center, angle, scale)
M = cv2.getAffineTransform() # 输入三对点(变换前和变换后)
affine = cv2.warpAffine(img, M, (w, h), borderMode) # 仿射变换
M = cv2.getPerspectiveTransform() # 输入四对点
affine = cv2.warpPerspective(img, M, (w, h), borderMode) # 透视变换·密集
findhomography() perspectiveTransform() # 透视变换·稀疏 ? 有什么区别
# borderMode: BORDER_REPLICATE 重复边界像素 BORDER_CONSTANT 填充固定值,默认 0 BORDER_ISOLATED 有效区域
# BORDER_REFLECT 反射 BORDER_REFLECT101 / BORDER_DEFAULT 对称
仿射变换:3 点确定一平面,由一组线性变换(缩放、平移和旋转)组成;(就是方形到平行四边形的变化)
透视变换:4 点确定三维面,实现了透视的效果(扭曲)
插值运算
pyrUp
pyrDown
resize
一维插值
方法 | 说明 | 特点 |
---|---|---|
最近邻 | 坐标四舍五入 | |
线性 | 相邻两个点加权求和,权重为相邻点与当前点x方向的距离 | 快 |
区域 | - | 下采样效果好 |
三次样条 | - | 上采样效果还,慢 |
二维插值
方法 | 说明 | 特点 |
---|---|---|
二维最近邻 | 两个方向坐标四舍五入 | 马赛克重 |
双线性 | 相邻4个点加权求和,权重为相邻点与当前点在x,y方向的距离 | |
双三次样条 | - |
HOG算法原理描述
梯度直方图(Histogram of Oriented Gradient, HOG)特征:先将原图划分成多个区域,然后球每个区域的梯度,再求直方图,构成图像的特征;在深度学习取得成功之前;是传统图像中用来进行物体检测的特征描述子,在 DL 之前,常结合 SVM 分类器用来做目标检测;在行人检测中获得了较大的成功;
原理:HOG 认为物体的局部形状可以被边缘的分布所描述;因此对原图等分,然后求子区域的梯度直方图,最终把所有区域的直方图连接在一起;为了提升对照射/阴影的不变性,可以对大的区域取梯度均值,用来给内部的子区域做归一化;
优势:与其他描述子相比,HOG拥有几何和光学转化不变性(除非物体方向改变);因此HOG描述子尤其适合人的检测;
过程:
- 灰度化
- 划分成小 cells
- 计算每个 cell 的梯度
- 统计每个 cell 的梯度直方图,就是 cell 的描述子
附录
A 代码示例
噪声(python)
#!/usr/bin/python3 python
# encoding: utf-8
import cv2
from random import uniform, randint
import numpy as np
__all__ = ['add_noise', 'add_gaussian_noise', 'add_salt_noise']
def transparent(img):
return 4 == img.shape[2]
def random_set_pixel(img, n, value):
n = int(n)
w, h, c = img.shape
for i in range(n):
i, j = randint(0, w-1), randint(0, h-1)
img[i, j, :] = value
def add_salt_noise(img, rate=0.5):
w, h, c = img.shape
n = max(w*h*rate, 20)
out = img.copy()
random_set_pixel(out, n, randint(0, 255)) # can use 255
random_set_pixel(out, n, randint(0, 255)) # can use 0
# keep transparent
if transparent(img):
out[:, :, 3] = img[:, :, 3]
return out
def add_gaussian_noise(img, mean=0, var=0.001, gray=0.5):
if uniform(0, 1) < gray:
noise = np.random.normal(mean, var ** 0.5, (img.shape[0], img.shape[1])) * 60
noise = np.tile(noise[:, :, np.newaxis], (1, 1, img.shape[2]))
else:
noise = np.random.normal(mean, var ** 0.5, img.shape) * 60
img = img.astype(np.int16)
noise = noise.astype(np.int16)
if transparent(img):
noise[:, :, 3] = img[:, :, 3]
img = cv2.add(img, noise)
img = np.clip(img, 0, 255)
img = img.astype(np.uint8)
return img
def add_noise(img, salt_rate=0.5, gaussian_rate=1):
if uniform(0, 1) < gaussian_rate:
img = add_gaussian_noise(img, mean=uniform(0.1, 2), var=uniform(0.002, 1), gray=0.9)
if uniform(0, 1) < salt_rate:
img = add_salt_noise(img, uniform(0.05, 0.3))
return img
噪声(c++) 【OpenCV】给图像添加噪声
仿射变换(python)
#!/usr/bin/python3 python
# encoding: utf-8
import cv2
from random import uniform, randint
import numpy as np
__all__ = ['crop_foreground', 'random_affine']
def transparent(img):
return 4 == img.shape[2]
def crop_foreground(img):
if transparent(img):
gray = img[:, :, 3]
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 30))
gray = cv2.dilate(gray, kernel)
gray = cv2.copyMakeBorder(gray, 1, 1, 1, 1, cv2.BORDER_CONSTANT)
contours, hierarchy = cv2.findContours(gray, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
if len(contours) == 0: # why
return img
x, y, w, h = cv2.boundingRect(contours[0])
img_valid = img[y:y+h, x:x+w, :]
return img_valid
else:
return img
def random_affine(img, crop=True, plain=False):
w, h, c = img.shape
top_left = (uniform(0, 0.3), uniform(0, 0.3))
bottom_left = (uniform(0, 0.5), uniform(0.6, 1))
top_right = (uniform(max(bottom_left[0], top_left[0]) +0.3, 1), uniform(0, bottom_left[1]-0.3))
bottom_right = (uniform(max(bottom_left[0], top_left[0])+0.3, 1), uniform(max(top_right[1], top_left[1])+0.3, 1))
if transparent(img):
mode = cv2.BORDER_REPLICATE
else:
mode = cv2.BORDER_CONSTANT
if plain:
M = cv2.getAffineTransform(np.float32([(0,0), (w-1, 0), (0, h-1)]),
np.float32([(w*top_left[0], h*top_left[1]),
(w*top_right[0], h*top_right[1]),
(w*bottom_left[0], h*bottom_left[1])]))
affine = cv2.warpAffine(img, M, (w, h), borderMode=mode)
else:
M = cv2.getPerspectiveTransform(np.float32([(0,0), (w-1, 0), (0, h-1), (w-1, h-1)]),
np.float32([(w*top_left[0], h*top_left[1]),
(w*top_right[0], h*top_right[1]),
(w*bottom_left[0], h*bottom_left[1]),
(w*bottom_right[0], h*bottom_right[1])]))
affine = cv2.warpPerspective(img, M, (w, h), borderMode=mode)
if crop:
affine = crop_foreground(affine)
return affine
Comments