图像处理基础知识

1. 基本

🌟 简述 opencv 中有哪些模块?

  • core: 核心功能模块(1),包括 opnecv 基础数据结构,动态数据结构,绘图函数,数组操作函数等

  • imgproc:图像处理模块(2),包括线性非线性图像滤波, 图像几何变换,直方图相关,特征检测,目标检测。

  • highgui: 高层 GUI 用户界面, 包含媒体输入和输出,图像和视频编码, 图形交互界面的接口等

  • calib3d:主要是相机校准和三维重建(3)相关的内容, 包括**基本多视角几何算法, 单个立体摄像头标定,物体姿态估计, 立体相似性算法, 3D信息重建等。

  • features2d: 2D功能框架,包含特征检测和描述(4),特征检测通用接口,描述符提取器通用接口,关键点绘制函数和匹配功能绘制函数等

  • flann: 高维近似近邻快速搜索算法库 ml 机器学习模块 objectect: 目标检测模块,包含级联分类和latent SVM两个部分

  • photo: 包含图像修复和图像去噪两部分(5) stiching: 图像拼接模块

  • contrib: 主要包含最近添加的不稳定功能。 lagacy: 一些废弃的代码库 nonfree:一些具有专利的算法

  • gpu: 运用 GPU 加速模块 ocl: 运用 OpenCL 加速的模块

  • ml: 机器学习算法包(6)

2. 图像的读取和显示与 Mat 的理解

🌟 opencv里面为啥是 bgr 存储图片而不是人们常听的 rgb ?

​ OpenCV在 1999 年由 Intel 建立,当时主流的摄像头制造商和软件供应商提供的摄像头采集的图像的通道排列顺序为BGR,另外对于图片,位图BMP是最简单的,也是Windows显示图片的基本格式,其文件扩展名为*.BMP。在Windows下,任何格式的图片文件(包括视频播放)都要转化为位图才能显示出来,各种格式的图片文件也都是在位图格式的基础上采用不同的压缩算法生成的,值得注意的是位图 BMP 的格式就是BGR

🌟 opencv 如何读取内存图片 ? 如何将 buffer 类型转化为 mat 类型 ?

可以使用 imdecode 函数, 可以将字节流数据 buf 解码成数组形式的图像

std::vector<char> img_data;
std::ifstream file_contents("example.png");

file_contents >> std::noskipws;
std::copy(std::istream_iterator<char>(file_contents), std::istream_iterator<char>(), std::back_inserter(img_data));

Mat matrixJprg = imdecode(Mat(img_data), IMREAD_COLOR);
imwrite("result.jpg", matrixJprg);

🌟 opencv 如何读取 png 格式的图片?

使用 imread() 直接读取即可 (png为无损压缩, jpg为有损压缩)

cv::Mat srcImg = cv:imread(const string&filename, int flags)

其中 flags 可以取以下值:

cv::IMREAD_COLOR 三通道图像 cv::IMREAD_GRAYSCALE 单通道图像

cv::IMREAD_ANYCOLOR 通道数为文件实际通道数(不超过3)

cv::IMREAD_ANYDEPTH 允许加载超过 8bit 深度

cv::IMREAD_UNCHANGED 通道数为文件实际通道数,且允许超过 8 bit 深度

🌟 解释下 raw 图像和 rgb 图像的区别 ?

Raw 图像就是 CMOS 或者 CCD 图像感应器将捕捉到的光源信号转化为数字信号的原始数据。RAW 文件是一种记录了数码相机传感器的原始信息,同时记录了由相机拍摄所产生的一些原数据(Metadata,如ISO的设置、快门速度、光圈值、白平衡等)的文件。RAW是未经处理、也未经压缩的格式,可以把 RAW 概念化为“原始图像编码数据”或更形象的称为“数字底片”

​ 优势: RAW文件几乎是未经过处理而直接从 CCD 或 CMOS 上得到的信息,通过后期处理,摄影师能够最大限度地发挥自己的艺术才华。 RAW 文件并没有白平衡设置,但是真实的数据也没有被改变,就是说作者可以任意的调整色温和白平衡,并且是不会有图像质量损失的。

​ RGB 结构的意思就是,图片是由 1 个个像素点的数据序列组成,每个像素的数据由RGB各个色值组成。JPG 虽然有压缩算法,但是压缩前的数据依然是基于 RGB 结构进行的。  

🌟 opencv里面 Mat 有哪些构造函数?

主要分为三种:

(1) 默认构造函数 cv::Mat

(2) 指定类型、大小、维度以及初值的 构造函数 cv::Mat(int rows, int cols, int type) cv::Mat(int ndims, const int* sizes, int type)

(3) 复制构造函数 cv::Mat(const Mat&mat)

🌟 opencv 遍历像素的方式 ?

1. 数组遍历 img.at<typename>(i,j) img.data[index]

(1) uchar b1 = image.at<Vec3b>(i,j)[0]
(2) for (int i = 0; i < height; i++){
        for (int j = 0; j < width; j++){
            int index = i * width + j;
	    int b1 = (int)backImg.data[3 * index + 0];

2. 迭代器遍历

for(auto it = img.begin<Vec3b>(); it != img.end<Vec3b>(); it++)
    for(int k=0; k<3; k++){
        (*it)[k] = ...
    }

3. 指针遍历

for(int i=0; i < rows; i++){
    uchar *p = img.ptr<uchar>(i);

4.核心函数LUT

LUT是最被推荐的用于实现批量图像元素查找和更改操作图像方法。在图像处理中,对于一个给定的值,将其替换成其他的值是一个很常见的操作,OpenCV 提供里一个函数直接实现该操作,并不需要自己扫描图像,就是: operationsOnArrays:LUT() ,一个包含于core module的函数。首先我们建立一个mat型用于查表

operationsOnArrays:LUT() <lut>,一个包含于core module的函数。首先我们建立一个mat型用于查表
    Mat lookUpTable(1, 256, CV_8U);
    uchar* p = lookUpTable.data; 
    for( int i = 0; i < 256; ++i)
        p[i] = table[i];

然后我们调用函数 (I 是输入 J 是输出):

LUT(I, lookUpTable, J);

在以上四种图像遍历方法中,从效率来看使用 OpenCV 内置函数 LUT 可以获得最快的速度,这是因为OpenCV库可以通过英特尔线程架构启用多线程。其次,指针遍历最快,迭代器遍历次之,at方法遍历最慢。一般情况下,我们只有在对任意位置的像素进行读写时才考虑at方法。

🌟 说说 opencv 中 Mat 的深拷贝和浅拷贝?

答: 使用 Mat 的拷贝构造函数或者赋值运算符, 比如:

Mat A, C;

A = imread(“1.jpg”)

Mat B(A);

C = A;

A, B, C 全部指向同一个图像矩阵, 改变其中一个, 另外两个也都会跟着改变

使用 clone() 或者 copyTo() 则会另外开辟图像存储空间,相互之间没有影响, 例如:

Mat F = A.clone();

Mat G;

A.copyTo(G);

现在改变 F 则不会对G 有影响

🌟 opencv 中 CV_8UC3 代表什么意思? opencv 中的 Scalar 类?

- 8 代表使用8位的 unsigned char 型, 每个像素由三个元素组成三通道, 格式为: CV_[位数][带符号与否][类型前缀]C[通道数]

- Scalar() 表示具有4个元素的数组, 在 opencv 中被大量被用于传递像素值, 比如 GRB 颜色值。 如果用不到第三个参数, 则不需要写出来, 若只写三个参数, 则opencv 会认为只需要传递三个参数。

3. 颜色空间

🌟 简述常见的颜色系统?

  • RGB 是最常见的颜色系统,基于颜色的加法混色原理,从黑色不断叠加Red,Green,Blue的颜色,最终可以得到白色光
  • HSV 和 HLS 把颜色分解成色调、饱和度和亮度/明度,描述颜色更加自然,可以通过抛弃最后一个元素使算法对输入图像的光照条件不敏感
  • YCrCb 颜色系统在 JPEG 图像格式中广泛使用。
  • LAB: L为亮度,AB 为两种不同的色度, 前者是明度通道,后者是色彩通道,可以分离色度和亮度

🌟 图像超分辨率重建中,为什么只使用 YCrCb 中的 Y 通道? 参考论文: SRCNN

图像被转化为 YCbCr 色彩空间,尽管该网络只使用亮度通道(Y)。然后,网络的输出合并已插值的 CbCr 通道,输出最终彩色图像。我们选择这一步骤是因为我们感兴趣的不是颜色变化(存储在 CbCr 通道中的信息)而只是其亮度(Y 通道);根本原因在于相较于色差,人类视觉对亮度变化更为敏感。

🌟 用 OpenCV 可以方便的将彩色图片转为灰度图,如 cvtColor(imgColor, imgGray, CV_BGR2GRAY)。请问这里彩色到灰度的转换公式是: gray=0.299R+0.587G+0.114*B.

4. 滤波器和卷积

🌟 常用边缘检测有哪些算子,各有什么特性?

常见的边缘检测算子有: Sobel 算子、拉普拉斯算子、canny 算子。

sobel 算子:

垂直方向                   水平方向

​ Sobel 算子是一个主要用于边缘检测的离散微分算子,结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。在图像的任何一点使用此算子,都会产生对饮的梯度适量或者其法矢量。注意一下,当内核大小为3时,sobel 会产生比较明显的误差,为了解决这个问题,opencv 提供了 scharr 函数, 但是该函数只能作用于大小为3 的内核。该函数运算和sobel 函数一样快,但是结果更加准确。

拉普拉斯算子:

img

Canny 算子:

​ Canny 边缘检测算子是 John F.Canny 于 1986 年开发出来的一个多级边缘检测算法。更为重要的是 Canny 创立了边缘检测计算理论(Computational theory ofedge detection),解释了这项技术是如何工作的。Canny 边缘检测算法以 Canny 的名字命名,被很多人推崇为当今最优的边缘检测的算法。

Canny 边缘检测的步骤:

1.消除噪声。 一般情况下,使用高斯平滑滤波器卷积降噪。 如下显示了一个 size = 5 的高斯内核示例:

img

2.计算梯度幅值和方向。 此处,按照Sobel滤波器的步骤。

  Ⅰ.运用一对卷积阵列 (分别作用于 x 和 y 方向):

img

  Ⅱ. 使用下列公式计算梯度幅值和方向:

img

​ 梯度方向近似到四个可能角度之一(一般为0, 45, 90, 135)

3.非极大值抑制。 这一步排除非边缘像素, 仅仅保留了一些细线条(候选边缘)。

4.滞后阈值。最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):

  Ⅰ. 如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素。

  Ⅱ. 如果某一像素位置的幅值小于 低 阈值, 该像素被排除。

  Ⅲ. 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留。

tips:对于Canny函数的使用,推荐的高低阈值比在 2:1到3:1之间。

🌟 常用去噪滤波器 ?

均值滤波: 把每个像素都用周围的8个像素来做均值操作。可以平滑图像速度快,算法简单。不能很好地保护图像细节,在**图像去噪的同时也破坏了图像的细节部分,从而使得图像变模糊,不能很好的去除噪声**。

中值滤波: 中值滤波是一种典型的非线性滤波技术, 基本思想是用像素点的邻域灰度值的中值来提到像素点的灰度值,该方法在**去除脉冲噪声、椒盐噪声的同时又能保留图像的边缘细节**。但对方向性很强的指纹图像进行滤波处理时,有必要引入方向信息,即利用指纹方向图来指导中值滤波的进行。

高斯滤波:高斯滤波是对整幅图像进行加权平均的过程, 每一个像素点的值都由其本身和邻域内的其他像素值经过加权平均后得到。 高斯滤波的具体操作是用一个模板(或称为卷积) 扫描图像中的每一个像素, 用模板确定的领域内像素的加权灰度值去代替模板的中心像素点的值。

双边滤波和使用双边滤波的场景**? 双边滤波是一种非线性滤波方法, 同时考虑了空域信息和灰度相似性,达到保边去噪的目的, 具有简单,非迭代,局部的特点用在于需要保留边缘信息的图像去噪**。缺点是由于双边滤波保证了边缘信息,所以其保存了过多的高频信息,对于彩色图像的高频噪声,双边滤波不能够干净的滤去,只能对于低频信息进行较好的滤波。

🌟 简述一下图像处理中的膨胀和腐蚀操作?

​ 腐蚀和膨胀都是对图像的白色(高亮)部分而言。膨胀是图像中的高亮部分进行膨胀,类似于领域扩张,效果图拥有比原图更大的高亮区域;腐蚀是原图的高亮部分被腐蚀,类似于领域被蚕食,效果图拥有比原图更小的高亮区域。

​ 从数学的角度来说,膨胀就是求局部最大值, 并把这个值赋值给参考点指定像素, 这样会使图中Dev高亮区域逐渐增长,腐蚀与之相反。

🌟 简述开运算/闭运算的操作流程和使用场景?

​ 开运算就是先腐蚀后膨胀的过程。可以用来消除小物体, 在纤细点处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。

​ 闭运算就是先膨胀后腐蚀的过程, 可以用来排除小型黑色区域。

🌟 简述顶帽(礼帽)和黑帽运算的定义和使用场景?

顶帽(礼帽)是原图像与开运算的结果图之差。因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。 黑帽运算用来分离比邻近点暗一些的斑块, 效果图有比较好的轮廓。

🌟 简述形态学梯度的定义和使用场景?

​ 形态学梯度是膨胀图和腐蚀图之差, 对二值图进行这一操作可以将团块(Blob)的边缘突出出来, 可以用形态学梯度来保留物体的边缘轮廓。

5. 角点 & 关键点 & 边缘检测

🌟 简述 harris 角点检测算法原理和使用场景

​ harris 角点检测是一种基于灰度图像的角点提取算法,稳定性高,尤其对 L 型角点检测精度高, 但是由于采用高斯滤波,所以运算速度相对较慢,较低信息有丢失和位置偏移的现象,且角点提取有聚簇现象。

🌟 说说几种传统算法中常用的特征检测算法?

  • FAST: FAST Feature Detector
  • STAR: Star Feature Detector
  • SIFT: Scale Invariant Feature Transform
  • SURF: Speeded Up Robust Feature 加速版的具有鲁棒性的特征检测算法
  • ORB: 是 Oriented Brief 的简称, 是 BRIEF 算法的改进版, 综合性能相对比较好的算法。

🌟 简要阐述一下 SIFT 和 SURF 算法的异同点?

比较内容 SIFT SURF
尺度空间 DOG 与不同尺度的图片卷积 不同尺度的box filters 与原图片卷积
特征点检测 先进行非极大值抑制, 再去去除低对比度点。 再通过 Hessian 矩阵去除边缘的点 先利用Hessian 矩阵确定候选点, 然后进行非极大值抑制
方向 在正方形区域内统计梯度的幅值的直方图, 找max 对应的方向。 可以有多个方向 在圆形区域内, 计算各个扇形范围内的 x, y 方向上的 harr 小波响应, 找模最大的扇形方向
特征描述子 16 16 的采样点划分为 44 的区域, 计算每个区域的采样点的梯度方向和幅值, 统计成8bin 直方图, 一共 448=128维 2020 的区域划分为 44 的子区域, 每个区域找 5*5 个采样点, 计算采样点的harr 小波响应, 记录 ∑dx ∑dy ∑\ dx\ ∑\ dy\ , 一共 444 = 64 维。

🌟 比较一下 SIFT, HOG 和 LBP 这三个特征提取算法?

特征 优点 缺点 适用范围
SIFT (1) 对于旋转尺度缩放亮度变化保持不变(2) 抗遮挡 计算量大 图像匹配、三维建模
HOG 忽略了光照颜色对图像造成的影响, 使得图像所需要的特征数据的维度降低了 (1) 描述子生成过程冗长,导致速度慢,实时性差(2) 很难处理遮挡问题(3) 由于梯度的性质, 该描述子对噪点相当敏感 行人检测
LBP (1) 对光照不敏感(2) 运算速度快 对方向信息敏感 人脸识别、图像分类

🌟 LBP 原理?

🌟 HOG 特征计算过程,介绍一个应用HOG特征的应用? 讲讲HOG特征?他在dpm里面怎么设计的,你改过吗?HOG能检测边缘吗?里面的核函数是啥?那hog检测边缘和canny有啥区别?

🌟 SIFT特征提取算法,原理?

🌟 你说opencv里面的HOG+SVM效果很差?他就直接来了句为啥很差?差了就不改了?差了就要换其他方法?、

🌟 简述传统算法中边缘检测的一般步骤

(1) 滤波: 滤波去除噪声

(2) 增强: 增强边缘特征

(3) 检测: 将边缘通过某种方式提取出来, 完成边缘检测

6. 基本算法

🌟 简述一下什么是光流?

​ 光流法是目前图像运动分析的重要方法。 光流用来指定时变图像中模式的运动速度,因为物体在运动时,在图像上对应点的亮度模式也在运动,这种图像亮度模式的表现运动就是光流。 光流表达了图像的变化,包含了目标运动的信息,所以可以被观察者用来确定目标的运动情况。

🌟 简单叙述一下漫水填充法?

​ 漫水填充是用一种特定颜色填充连通区域, 通过设置可以连通像素的上下限以及连通方式来达到不同的填充效果的方法。漫水填充经常被用来标记或者分离图像的一部分,以便对其进行进一步处理或分析。简单来说, 就是自动选中和种子点相连的区域,接着讲该区域替换成指定的颜色。

🌟 简述霍夫变换的原理?

🌟 霍夫圆变换

​ 霍夫圆变换的基本原理和霍夫线变化大体上很相似,只是对应的二维极径极角空间被三维的圆心点x,y,半径r空间取代,这就意味着这是一个三维空间,所以需要大量的内存,执行效率低速度慢。

🌟 简述一下分水岭算法?

​ 分水岭算法是一种图像区域分割法, 在分割过程中,它会把临近像素间的相似性作为重要的参考依据,从而在空间位置上相近并且灰度值相近(求梯度)的像素点互相连接起来构成一个封闭的轮廓。分水岭算法常用的操作步骤: 彩色图像灰度化, 然后再求梯度,最后在梯度图的基础上进行分水岭算法,求得图像的边缘线。

🌟 简述一些图像金字塔的种类和区别?

一般情况下有两种图像金字塔, 分别是:

高斯金字塔: 用来向下采样

拉普拉斯金字塔: 用来从金字塔底层图像重建上层未采样图片, 可以对图像进行最大程度的还原,配合高斯金字塔一起使用

两者的区别在于: 高斯金字塔用来向下采样图像, 拉普拉斯金字塔用来从金字塔底层图像中向上采样,重建一个图像可以将拉普拉斯金字塔理解为高斯金字塔的逆形式。

🌟 简述凸包的定义?

​ 给定二维平面上的点集合, 凸包就是将最外层的点连接起来构成凸多边形, 是能包含点集中所有的点。理解物体形状或轮廓的一种比较有用的方法便是计算一个物体的凸包。

🌟 简述一下仿射变换的定义?

​ 仿射变换是指在几何中,一个向量空间进行一次线性变换并接上一个平移, 变换为一个向量空间的变换。它保持了二维图像的平直性(直线变换后仍然是直线)和平行性。一个任意的仿射变换都能便是为乘以一个矩阵(线性变换)接着再加上一个向量(平移)的形式。

🌟 简述反向投影的定义和使用场景?

​ 反向投影就是一种记录给定图像中的像素点如何适应直方图模型像素分布方式的一种方法。所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征的方法。反向投影用于在输入图像(通常较大)中查找特定图像(通常较小) 最匹配的点或者区域, 也就是定位模板图像出现在输入图像的位置。

7. 基本实现

注意,一下所有需要写代码的题目,不允许使用OpenCV的Mat类。如果图片内容需要用指针读取。

给定0-1矩阵,求连通域。(遇到过N次,笔试面试都有,最好做到能徒手hack代码或者伪代码。)

写一个函数,求灰度图的直方图。

写一个均值滤波(中值滤波)。

常用的特征提取方法。

常用的目标检测方法。

常用的边缘提取方法。

常用的插值方法。

常用的图像分割算法。

写一个图像resize函数(放大和缩小)。

彩色图像、灰度图像、二值图像和索引图像区别?(索引图像到底是啥?)

给定摄像头范围和图像大小求分辨率。

如何检测图片中的汽车,并识别车型,如果有遮挡怎么办?

(2) 常用滤波器原理,最好自己会手写一些就简单的

(3) 图像的仿射变换

9、用过opencv里面哪些函数? (我顺带回答了一下canny,HR又问opencv里面有c-a-n-n-y有这几个字母的函数吗,尴尬。。。又问我如何自己写canny边缘检测算法)

13、如何求一张图片的均值?(考虑了溢出和分块求解,貌似不满意。。。回头看看积分图里面如何解决溢出的。)

14、如何写程序将图像放大缩小?(我回答的插值,不太对。。。比如放大两倍可以插值,那放大1.1倍呢,)—>放大1.1倍也可以插值

15、如何遍历一遍求一张图片的方差?(回答的是采用积分图,并让我推导这样为啥可行。这个问题以前帮同学解决过。。。)

8. 工程经验

🌟 简述 .hpp 和 .h 的区别?

.hpp 的本质就是将 .cpp的实现代码混入 .h 头文件当中, 定义与实现都包含在同一文件,则该类的调用者只需要 include该 .hpp 文件即可,无需再将cpp加入project中进行编译。而实现代码直接编译到调用者的obj文件中, 不再生成单独的obj, 采用 hpp 将大幅度减小调用 project 中的 cpp 文件数和编译次数,也不用再发布 lib 与 dll 文件,因此非常适合用来编写公用的开源库。

🌟 Cpp 语言如果想用 OpenCV 的模块怎么用 ?


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!