2014-11-17 23:10:48 left_la 阅读数 20147
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14060 人正在学习 去看看 戴振良

在使用MATLAB语言实现图像处理算法时,我们常常会遇到需要对图像块(patch)进行运算的情况。受到其他语言编程习惯的影响,循环通常是不假思索的第一选择。但由于matlab是利于矩阵运算的编程语言,采用循环只会低效和耗时。

因此,下面总结了在图像处理中遇到的块运算常用的命令及代码,希望对大家有所帮助。

1. nlfilter

调用格式:B = nlfilter(A, [m n], fun);

意为对图像A的每个大小为m*n的patch进行fun函数的操作,patch的取法为滑动(sliding),即patch的中心像素遍历图像的每个点,当取到边界时需要进行边界延拓。fun必须是函数的句柄,可自由定义也可取matlab内置的函数。

例:

对图像I的每个5*5邻域进行取标准差的运算 :I2= nfilter(I,[5,5],@std2);

2. blockproc

不重叠的块运算命令(distinct block processing)

调用格式:B = blockproc(A,[M N],fun);

意为对图像A的每个不重叠的大小为m*n的块进行fun函数的操作,级联的结果为B。

例:

对图像I的每个5*5邻域进行取标准差的运算:fun = @(block_struct) ...

   std2(block_struct.data) * ones(size(block_struct.data));

I2 = blockproc(I,[5 5],fun);

另:

还有个命令blkproc,功能已被matlab不推荐使用,不再赘述。

3. colfilter

调用格式:B = colfilt(A,[m n],block_type,fun)

意为将图像A重排成每列为m*n块拉成一列组成的临时矩阵,对该临时矩阵进行fun函数的操作。

例:

对图像的I的每个5*5邻域进行取标准差的运算 :I2 = uint8(colfilt(I,[5 5],’sliding',@std));

另:

在调用fun函数处理前,colfilt先调用了im2col生成了临时矩阵,调用fun函数处理之后,又调用col2im将成列的矩阵重排为块。

colfilt与blockproc及nlfilter相比,用法类似,但速度更快。

4. im2col and col2im

im2col将图像分块拉成列重排。patch取法有两种:’distinct’和’sliding’。

调用格式:B = im2col(A,[m n],block_type)

例:

  img = im2double(imread(‘lena.png’));

  p  = im2col(img , [y, x], ‘sliding’);

将大小为Y*X的图像分为y*x的块,imcol生成的B矩阵的大小为(Y-y+1)*(X-x+1).

若要得到与原图同样大小的图像,col2im做不到,你需要下列函数。


 实现真正的“col2im”功能的函数

下载工具箱 http://hvrl.ics.keio.ac.jp/charmie/file/demo_patching.zip

将demo_patching.m的第二行改为if(nargin < 4), border = ‘on';你就可以随意使用啦!

2018-10-04 22:04:45 qq_39747606 阅读数 24
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14060 人正在学习 去看看 戴振良

**

  • 图像处理学习笔记 10.4

**
每个图像的每一个点都可以分为信号强度,信号结构,平均强度;其实就是用三个参数将每一个patch线性表示出来。信号结构作为信号强度的权重值,平均强度作为一个惩罚函数对效果进行调节,所以主要研究的还是信号强度这一个变量,而信号强度与局部对比度密切相关,所以可以用局部对比度相关的点来研究信号强度。而对于局部对比度的话,一般选择对比度最高的一个patch来使用。故我觉得算法的核心主要是寻找对比度最高的那个patch,然后对权重、惩罚函数进行设定。
目前存在的问题有,这个线性函数的对应关系是什么还有点模糊,是图像同一个点的每一个patch都有一个线性表示还是如何?明天需要对这个问题进行一次确认。
继续了论文的研究,代码区可以分为工程部分与算法部分,目前需要做的就是将工程部分先搁置一下,着重研究一下算法部分。明天将先在论文中明白各个参数的意义与作用,这一点应该是研究算法的核心,并将其代入算法来将算法大致通览一遍。也许到最后学习完这个算法就是学会参数如何调整之类的,然后去使用。

//这里说句题外话,关于大数据知识的学习与其他方面的编程学习,我还是觉得平行任务越少越好。如果图像学好了,那么对工程项目的代码分析,或者对算法的研究都可以得到提升。如果后期有兴趣的话可以去学习别的方面的编程知识吧,我觉得,人还是不要贪多,大一一年的经历告诉我没有用。

2019-05-12 20:52:04 weixin_43795395 阅读数 354
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14060 人正在学习 去看看 戴振良

C  图像处理 

 

准备把这一篇写完后,SLAM求职专题系列就告一段落,还剩下E篇关于机器学习和深度学习暂不写了。      


目录

C  图像处理 

 

1. 图像平滑算子、边缘检测算子

2. 图像去噪滤波算法(高斯、均值、双边、Guide filter)

3. 三个度量patch相似度的方法(SSD、SAD、NCC)

4. 二进制描述子

5. SIFT的4个不变性

6. 特征点、描述子ORB、SIFT、SURF、BRIEF等等 。

7. Mat实现、Mat类指针引用复制函数

8. 颜色直方图统计,手撕代码

9. 形态学操作,手撕代码

10. 积分图,手撕代码

11. 连通区域算法,给二值图,求出最大联通区域(用深度优先和广度优先算法,手撕代码)

12. Mser、Swt检测

13. 图像分割(Grabcut)

14. 目标跟踪(相关滤波KCF)


1. 图像平滑算子、边缘检测算子

图像处理里面的概念不少,很容易混为一谈。所以这里我将结合《计算机视觉算法与应用》以及一些网上的博客,尽可能把这些概念用通俗的语言理清。

1. 领域算子:也叫局部算子。利用像素周围的值决定次像素的最终输出值。 

  •  线性算子(滤波)是一种常用的领域算子,像素的输出值取决于一小领域内输入像素的加权和    

其中的h为权重核或者叫滤波系数。    

  • 非线性算子,例如形态学运算、距离变换等。  

 以上分别是一些领域算子:a. 原始图像 b. 平滑 c. 锐化 d. 保边平滑滤波 e. 图像的二值化 f. 膨胀 g. 距离变换 h. 连通量  

 

2. 可分离的滤波(都是线性滤波)

卷积运算每个像素需要K^2操作,K是卷积核的大小。如果一个卷积核可以采用如下方式操作可加快速度,称为可分离的: 线用一维行向量进行卷积,再用一维列向量进行卷积(总共需要2K操作)。  

以上这些是常用的可分离的线性滤波器。(第一个我们可以叫方框滤波器,有些地方也叫归一化滤波)

 

3. 拉普拉斯算子 

拉普拉斯滤波器跟上面的以上滤波器的不同在于它是一种高通滤波器,能够保留图像的高频成分。图像的二阶倒数称为“Laplacian”算子   

 

  • 离散函数导数

离散函数的导数退化成了差分,一维一阶差分公式和二阶差分公式分别为

  • Laplace算子的差分形式

分别对Laplace算子x,y两个方向的二阶导数进行差分就得到了离散函数的Laplace算子。在一个二维函数f(x,y)中,x,y两个方向的二阶差分分别为

 

写成filter mask的形式如下:

该mask的特点,mask在上下左右四个90度的方向上结果相同,也就是说在90度方向上无方向性。为了让该mask在45度的方向上也具有该性质,对该filter mask进行扩展定义为

 

4. 图像平滑与锐化   

  • 平滑核函数常用于减少高频噪声,使图片变得有些模糊,低通滤波。灰度突变在频域中代表了一种高频分量,低通滤波器的作用就是滤掉高频分量,从而达到减少图象噪声的目的。常用的平滑算子有: (1)方框滤波; (2)高斯滤波; (3)中值滤波
  • 锐化就是通过增强高频分量来减少图象中的模糊,因此又称为高通滤波。锐化处理在增强图象边缘的同时增加了图象的噪声。。常用的锐化操作用拉普拉斯(Laplacian)算子。

 

5. 非线性滤波 (比如在噪声是散粒噪声而不是高斯噪声,即图像偶尔会出现很大的值的时候,用非线性滤波更好)

  • 中值滤波:中值滤波(Median filter)是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像边缘细节。
  • 双边滤波:双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。具有简单、非迭代、局部的特点。
  • 具体可见(https://www.jianshu.com/p/09f961df9c6c

 

6. 边缘检测算子—— 

图来自 https://blog.csdn.net/LilyNothing/article/details/78996239

è¾¹ç¼æ£æµç®å­

具体你可再参考: 

图像处理常用边缘检测算子

https://blog.csdn.net/green_master/article/details/52504575

几种边缘检测算子的比较Roberts,Sobel,Prewitt,LOG,Canny

https://blog.csdn.net/gdut2015go/article/details/46779251

 

 

2. 图像去噪滤波算法(高斯、均值、双边、Guide filter)

上面的问题中我们已经把常见的一些图像处理方法以及滤波的概念介绍了。   

 

参考: 图像去噪算法简介(https://blog.csdn.net/eric_e/article/details/79504444

图像去噪算法分类——

(1) 空间域滤波

空域滤波是在原图像上直接进行数据运算,对像素的灰度值进行处理。常见的空间域图像去噪算法有邻域平均法、中值滤波、低通滤波等。

(2) 变换域滤波

图像变换域去噪方法是对图像进行某种变换,将图像从空间域转换到变换域,再对变换域中的变换系数进行处理,再进行反变换将图像从变换域转换到空间域来达到去除图像嗓声的目的。将图像从空间域转换到变换域的变换方法很多,如傅立叶变换、沃尔什-哈达玛变换、余弦变换、K-L变换以及小波变换等。而傅立叶变换和小波变换则是常见的用于图像去噪的变换方法。

(3) 偏微分方程

偏微分方程是近年来兴起的一种图像处理方法,主要针对低层图像处理并取得了很好的效果。偏微分方程具有各向异性的特点,应用在图像去噪中,可以在去除噪声的同时,很好的保持边缘。偏微分方程的应用主要可以分为两类:一种是基本的迭代格式,通过随时间变化的更新,使得图像向所要得到的效果逐渐逼近,这种算法的代表为Perona和Malik的方程[27],以及对其改进后的后续工作。该方法在确定扩散系数时有很大的选择空间,在前向扩散的同时具有后向扩散的功能,所以,具有平滑图像和将边缘尖锐化的能力。偏微分方程在低噪声密度的图像处理中取得了较好的效果,但是在处理高噪声密度图像时去噪效果不好,而且处理时间明显高出许多。

(4) 变分法

另一种利用数学进行图像去噪方法是基于变分法的思想,确定图像的能量函数,通过对能量函数的最小化工作,使得图像达到平滑状态,现在得到广泛应用的全变分TV模型就是这一类。这类方法的关键是找到合适的能量方程,保证演化的稳定性,获得理想的结果。

(5) 形态学噪声滤除器

将开与闭结合可用来滤除噪声,首先对有噪声图像进行开运算,可选择结构要素矩阵比噪声尺寸大,因而开运算的结果是将背景噪声去除;再对前一步得到的图像进行闭运算,将图像上的噪声去掉。据此可知,此方法适用的图像类型是图像中的对象尺寸都比较大,且没有微小细节,对这类图像除噪效果会较好。

 

针对这道题,应该主要考察空间域上的去噪滤波算法—— 

经典的图像去噪算法有: 均值滤波,高斯滤波,双边滤波,中值滤波  (具体还有不明的可以参见上个问题或者自己查阅网络)

 

 

3. 三个度量patch相似度的方法(SSD、SAD、NCC)

参考《slam十四讲》中的内容—— 

极线搜索与块匹配: 在极线上搜索某一像素的匹配时,单个像素的亮度很容易没有区分度,因此取像素的周围小窗口提高区分度,所谓的块匹配,算法的假设从像素的灰度不变性变为了图像块的灰度不变性

把p1周围的小块记做A(i,j),候选的匹配块为B(i,j) 。这里介绍三种相似度的计算方法

1. SAD ( sum of absolute difference ) 

2. SSD ( sum of squared distance )

3. NCC ( normalized cross correlation )

接近1,表示相似

 

 

 

4. 二进制描述子

SIFT和SURF介绍

https://www.cnblogs.com/hepc/p/9636474.html

BRIEF描述子——

    BRIEF(Binary Robust Independent Elementary Features)是一种对已检测到的特征点进行表示和描述的特征描述方法,和传统的利用图像局部邻域的灰度直方图或梯度直方图提取特征的方式不同,BRIEF是一种二进制编码的特征描述子,既降低了存储空间的需求,提升了特征描述子生成的速度,也减少了特征匹配时所需的时间。 
原理概述
  经典的图像特征描述子SIFT和SURF采用128维(SIFT)或者64维(SURF)特征向量,每维数据一般占用4个字节(Byte),一个特征点的特征描述向量需要占用512或者256个字节。如果一幅图像中包含有大量的特征点,那么特征描述子将占用大量的存储,而且生成描述子的过程也会相当耗时。在SIFT特征的实际应用中,可以采用PCA、LDA等特征降维的方法来减少特征描述子的维度,例如PCA-SIFT;此外还可以采用一些局部敏感哈希(Locality-Sensitive Hashing, LSH)的方法将特征描述子编码为二进制串,然后使用汉明距离(Hamming Distance)进行特征点的匹配,汉明距离计算的是两个二进制比特串中同一位置不同值的个数,可通过异或操作快速实现,大大提升了特征匹配的效率。

  BRIEF正是这样一种基于二进制编码生成特征描述子,以及利用汉明距离进行特征匹配的算法。由于BRIEF只是一种特征描述子,因此事先得检测并定位特征点,可采用Harris、FAST或者是SIFT算法检测特征点,在此基础上利用BRIEF算法建立特征描述符,在特征点邻域Patch内随机选取若干点对(p,q)(p,q),并比较这些点对的灰度值,若I(p)>I(q)I(p)>I(q),则编码为1,否则编码为0。这样便可得到一个特定长度的二进制编码串,即BRIEF特征描述子。
---------------------  
原文:https://blog.csdn.net/Zachary_Co/article/details/78867059 

 

二进制描述子有: BRIEF(ORB用到)、 BRISK, FREAK。

 BRISK算法是2011年ICCV上《BRISK:Binary Robust Invariant Scalable Keypoints》文章中,提出来的一种特征提取算法,也是一种二进制的特征描述算子。它具有较好的旋转不变性、尺度不变性,较好的鲁棒性等。在图像配准应用中,速度比较:SIFT<SURF<BRISK<FREAK<ORB,在对有较大模糊的图像配准时,BRISK算法在其中表现最为出色。

 

 

5. SIFT的4个不变性

  • 尺度不变性
  • 旋转不变性
  • 亮度不变性 
  • 平移不变性

 

 

6. 特征点、描述子ORB、SIFT、SURF、BRIEF等等 。

geometric invariance:平移,旋转,尺度……; photometric invariance:亮度,曝光……

    计算速度:            ORB>>SURF>>SIFT(各差一个量级)

    旋转鲁棒性:        SURF>ORB~SIFT(表示差不多)

    模糊鲁棒性:        SURF>ORB~SIFT

    尺度变换鲁棒性: SURF>SIFT>ORB(ORB并不具备尺度变换性)

 

SIFT、SURF、Harris、BRIEF、FAST、DAISY、FAST等描述符介绍

https://blog.csdn.net/qq_29828623/article/details/52403562

 

 

7. Mat实现、Mat类指针引用复制函数

实现好像不用吧?  这里我们主要把时间用来学习OpenCV中的Mat类——

 

Opencv中的Mat类使用方法总结:https://blog.csdn.net/chen134225/article/details/80787665

OpenCV学习笔记(04):Mat类详解(一):https://blog.csdn.net/CV_Jason/article/details/54928920

Opencv Mat的三种常用类型简介:https://blog.csdn.net/Elen005/article/details/80066073

 

 

8. 颜色直方图统计,手撕代码

题目可能是要考察OpenCV的使用吧。。

颜色直方图在OpenCV的的实现——

void calcHist(const Mat* arrays, 
              int narrays, 
              const int* channels, 
              InputArray mask,
              OutputArray hist, 
              int dims, 
              const int* histSize, 
              const float** ranges, 
              bool uniform=true, 
              bool accumulate=false )

parameters: 

  1. 第一个参数:源输入(图像)数组,必须是深度和大小均相同的CV_8U或者CV_32F(即uchar或者float)图片,每一个可以是任意通道的; 
  2. 第二个参数:源输入数组中的图片个数; 
  3. 第三个参数:用来计算直方图的通道维数的数组,第一张图片的通道由0到arrays[0].channels()-1列出,第二张图片的通道从arrays[0].channels()到arrays[0].channels()+arrays[1].channels()-1,以此类推,而该参数就是从通道序列中选取一子序列,参与直方图计算的是子序列而不是所有通道序列; 
  4. 第四个参数:可选的掩模,如果该矩阵不是空的,则必须是8位的并且与arrays[i]的大小相等,掩模的非零值标记需要在直方图中统计的数组元素; 
  5. 第五个参数:输出直方图,是一个稠密或者稀疏的dims维的数组; 
  6. 第六个参数:直方图的维数,必须为正,并且不大于CV_MAX_DIMS(当前的OpenCV版本中为36,即最大可以统计36维的直方图); 
  7. 第七个参数:用于指出直方图数组每一维的大小的数组,即指出每一维的bin的个数的数组; 
  8. 第八个参数:用于指出直方图每一维的每个bin的上下界范围(数组)的数组,当直方图是均匀的(uniform =true)时,对每一维i指定直方图的第0个bin的下界和最后一个即第histSize[i]-1个bin的上界,也就是说对均匀直方图来说,每一个ranges[i]都是一个两个元素的数组【指出该维的上下界】。当直方图不是均匀的时,每一个ranges[i]数组都包含histSize[i]+1个元素; 
  9. 第九个参数:直方图是否均匀的标志;【指定直方图每个bin统计的是否是相同数量的灰度级】; 
  10. 第十个参数:累加标志; 

calcHist函数的channels参数和narrays以及dims共同来确定用于计算直方图的图像;首先dims是最终的直方图维数,narrays指出了arrays数组中图像的个数,其中每一幅图像都可以是任意通道的【只要最终dims不超过36即可】。

如果channels参数为0,则narrays和dims必须相等,否则弹出assert,此时计算直方图的时候取数组中每幅图像的第0通道。当channels不是0的时候,用于计算直方图的图像是arrays中由channels指定的通道的图像,channels与arrays中的图像的对应关系,如channels的参数说明的,将arrays中的图像从第0幅开始按照通道摊开排列起来,然后channels中的指定的用于计算直方图的就是这些摊开的通道。

假设有arrays中只有一幅三通道的图像image,那么narrays应该为1,如果是想计算3维直方图【最大也只能是3维的】,想将image的通道2作为第一维,通道0作为第二维,通道1作为第三维,则可以将channels设置为channesl={2,0,1};这样calcHist函数计算时就按照这个顺序来统计直方图。

 OpenCV中计算RGB空间的颜色直方图—— 

//计算图像RGB空间的颜色直方图
#include<opencv2\opencv.hpp>  
#include<iostream>  
using namespace std;
using namespace cv;
 
class HistogramND {
private:
	Mat image;//源图像  
	int hisSize[1], hisWidth, hisHeight;//直方图的大小,宽度和高度  
	float range[2];//直方图取值范围  
	const float *ranges;
	Mat channelsRGB[3];//分离的BGR通道  
	MatND outputRGB[3];//输出直方图分量  
public:
	HistogramND() {
		hisSize[0] = 256;
		hisWidth = 400;
		hisHeight = 400;
		range[0] = 0.0;
		range[1] = 255.0;
		ranges = &range[0];
	}
 
	//导入图片  
	bool importImage(String path) {
		image = imread(path);
		if (!image.data)
			return false;
		return true;
	}
 
	//分离通道  
	void splitChannels() {
		split(image, channelsRGB);
	}
 
	//计算直方图  
	void getHistogram() {
		calcHist(&channelsRGB[0], 1, 0, Mat(), outputRGB[0], 1, hisSize, &ranges);
		calcHist(&channelsRGB[1], 1, 0, Mat(), outputRGB[1], 1, hisSize, &ranges);
		calcHist(&channelsRGB[2], 1, 0, Mat(), outputRGB[2], 1, hisSize, &ranges);
 
		//输出各个bin的值  
		for (int i = 0; i < hisSize[0]; ++i) {
			cout << i << "   B:" << outputRGB[0].at<float>(i);
			cout << "   G:" << outputRGB[1].at<float>(i);
			cout << "   R:" << outputRGB[2].at<float>(i) << endl;
		}
	}
 
	//显示直方图  
	void displayHisttogram() {
		Mat rgbHist[3];
		for (int i = 0; i < 3; i++)
		{
			rgbHist[i] = Mat(hisWidth, hisHeight, CV_8UC3, Scalar::all(0));
		}
		normalize(outputRGB[0], outputRGB[0], 0, hisWidth - 20, NORM_MINMAX);
		normalize(outputRGB[1], outputRGB[1], 0, hisWidth - 20, NORM_MINMAX);
		normalize(outputRGB[2], outputRGB[2], 0, hisWidth - 20, NORM_MINMAX);
		for (int i = 0; i < hisSize[0]; i++)
		{
			int val = saturate_cast<int>(outputRGB[0].at<float>(i));
			rectangle(rgbHist[0], Point(i * 2 + 10, rgbHist[0].rows), Point((i + 1) * 2 + 10, rgbHist[0].rows - val), Scalar(0, 0, 255), 1, 8);
			val = saturate_cast<int>(outputRGB[1].at<float>(i));
			rectangle(rgbHist[1], Point(i * 2 + 10, rgbHist[1].rows), Point((i + 1) * 2 + 10, rgbHist[1].rows - val), Scalar(0, 255, 0), 1, 8);
			val = saturate_cast<int>(outputRGB[2].at<float>(i));
			rectangle(rgbHist[2], Point(i * 2 + 10, rgbHist[2].rows), Point((i + 1) * 2 + 10, rgbHist[2].rows - val), Scalar(255, 0, 0), 1, 8);
		}
 
		cv::imshow("R", rgbHist[0]);
		imshow("G", rgbHist[1]);
		imshow("B", rgbHist[2]);
		imshow("image", image);
	}
};
 
 
int main() {
	string path = "C:\\Users\\59235\\Desktop\\image\\girl1.jpg";
	HistogramND hist;
	if (!hist.importImage(path)) {
		cout << "Import Error!" << endl;
		return -1;
	}
	hist.splitChannels();
	hist.getHistogram();
	hist.displayHisttogram();
	waitKey(0);
	return 0;
}

(参考https://blog.csdn.net/zhu_hongji/article/details/80443585

 

统计颜色直方图方法一:采用三维数组a[][][]来遍历Mat每一个像素

vector<float> getHist1(Mat testImg)//RGB色彩空间的(4,4,4)模型。
{
	const int div = 64;
	const int bin_num = 256 / div;
	int length = bin_num*bin_num*bin_num;
	Vec3i pix;
	vector<float> b;
	float a[bin_num*bin_num*bin_num] = { 0 };
	float d[bin_num][bin_num][bin_num] = { 0 };
 
	for (int r = 0; r < testImg.rows; r++)
	{
		for (int c = 0; c < testImg.cols; c++)
		{
			pix = testImg.at<Vec3b>(r, c);
			pix.val[0] = pix.val[0] / div;
			pix.val[1] = pix.val[1] / div;
			pix.val[2] = pix.val[2] / div;
			//	testImg.at<Vec3b>(r, c) = pix;
			d[pix.val[0]][pix.val[1]][pix.val[2]]++;
		}
	}
	float sum = 0;
	for (int i = 0; i < bin_num; i++)
	for (int j = 0; j < bin_num; j++)
	for (int k = 0; k < bin_num; k++){
		a[i*bin_num*bin_num + j*bin_num + k] = d[i][j][k];
		sum += d[i][j][k];
		cout << d[i][j][k] << " ";
	}
	for (int i = 0; i < length; i++)//归一化
	{
		a[i] = a[i] / sum;
		b.push_back(a[i]);
	}
	return b;
}

这道题目我们主要学习的是以上的代码,如果对其中的概念还有所不理解,那么请看下面的链接中的例子,会有较直观的认识: http://www.cnblogs.com/ldxsuanfa/p/9942564.html

 

统计颜色直方图方法二:采用一维数组a[]来遍历Mat每一个像素

vector<float> getHist2(Mat &image)//RGB色彩空间的(4,4,4)模型。
{
	const int div = 64;
	const int bin_num = 256 / div;
	int nr = image.rows; // number of rows
	int nc = image.cols; // number of columns
	if (image.isContinuous())  {
		// then no padded pixels  
		nc = nc*nr;
		nr = 1;  // it is now a 1D array  
	}
	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0));// mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0
	int b, g, r;
	vector<float> bin_hist;
	int ord = 0;
	float a[bin_num*bin_num*bin_num] = { 0 };
	for (int j = 0; j < nr; j++) {
		const uchar* idata = image.ptr<uchar>(j);
		for (int i = 0; i < nc; i++) {
			b = ((*idata++)&mask) / div;
			g = ((*idata++)&mask) / div;
			r = ((*idata++)&mask) / div;
			ord = b * bin_num * bin_num + g * bin_num + r;
			a[ord] += 1;
		} // end of row      
	}
	float sum = 0;
//	cout << "a[i]2:" << endl;
	for (int i = 0; i < div; i++)
	{
		sum += a[i];
//		cout << a[i] << " ";
	}
	for (int i = 0; i < div; i++)//归一化
	{
		a[i] = a[i] / sum;
		bin_hist.push_back(a[i]);
	}
	return bin_hist;
 
}

(参考https://blog.csdn.net/dyx810601/article/details/50520243

 

 

9. 形态学操作,手撕代码

图像形态学操作是基于形状的一系列图像处理操作的合集,主要是基于集合论基础上的形态学数学。主要有四个操作:膨胀,腐蚀,开,闭。 这个问题似乎不需要自己实现形态学的代码吧,我们这里主要贴两篇博客,讲形态学的概念以及OpenCV中的实现!  

形态学操作、腐蚀、膨胀、开运算、闭运算http://blog.sina.com.cn/s/blog_159aff7940102xdf4.html

形态学操作https://www.jianshu.com/p/29eddeddcccb

 

 

10. 积分图,手撕代码

积分图简介—— 

有时候只需要计算图像中某个特定区域的直方图。实际上累计图像的某个子区域内的像素总和,是很多计算机视觉算法中常见的过程。现在假设需要对图像中的多个兴趣区域计算几个此类直方图。这些计算过程都马上会变得非常耗时。这种情况下,有一个工具可以极大地提高统计图像子区域像素的效率:积分图像。

积分图的定义: 取图像左上侧的全部像素计算累加和,并用这个累加和替换图像中的每一个像素,用这种方式得到的图像称为积分图像。为了防止溢出,积分图像的值通常采用int类型或float类型。

如果知道了积分图,就可以很容易的计算出图下A区域的像素之和。 

 

求图像积分图的代码——  

其中要注意,W×H的图像,积分图为(W+1)×(H+1),  其中的第一列和第一行都是0。原图像是用一维数组来存储像素的。     

void GetGrayIntegralImage(unsigned char *Src, int *Integral, int Width, int Height)
{
    memset(Integral, 0, (Width + 1) * sizeof(int));                    //    第一行都为0
    for (int Y = 0; Y < Height; Y++)
    {
        unsigned char *LineSrc = Src + Y * Width;      //原图像中每一行的起始位置 
        int *LinePre = Integral + Y * (Width + 1) + 1;                  //    上一行位置            
        int *LineCur = Integral + (Y + 1) * (Width + 1) + 1;            //    当前位置,注意每行的第一列的值都为0
        LineCur[-1] = 0;                                                //    第一列的值为0
        //这一步我也是头一次见,数组的下标可以为负数,表示内存中的上一位置  
        for (int X = 0, Sum = 0; X < Width; X++)
        {
            Sum += LineSrc[X];                                        //    行方向累加
            LineCur[X] = LinePre[X] + Sum;                            //    更新积分图
        }
    }
}

(来自https://www.cnblogs.com/Imageshop/p/6219990.html

注意原文中函数参数列表中还有一个参数是stride,应该指的是步长,但是一般都是每一行都要进行积分,所以代码中我将这个参数取消掉了。而且原文作者还给了更高效的解法,略过 。 

 

 

11. 连通区域算法,给二值图,求出最大联通区域(用深度优先和广度优先算法,手撕代码)

这个问题在SLAM篇(D1)中已经解答过。 

 

 

12. Mser、Swt检测

先来看一篇检测算法的比较: 

图像处理中,SIFT,FAST,MSER,STAR等特征提取算法的比较与分析

https://blog.csdn.net/Real_Myth/article/details/51305908 

再来看一篇关于检测文本的:     

MSER+NMS检测图像中文本区域 

https://www.jianshu.com/p/b5af24e2f9ff

1. MSER

MSER(Maximally Stable Extrernal Regions)是区域检测中影响最大的算法。

MSER基于分水岭的概念:对图像进行二值化,二值化阈值取[0, 255],这样二值化图像就经历一个从全黑到全白的过程(就像水位不断上升的俯瞰图)。在这个过程中,有些连通区域面积随阈值上升的变化很小,这种区域就叫MSER。

,其中Qi表示第i个连通区域的面积,Δ表示微小的阈值变化(注水),当vi小于给定阈值时认为该区域为MSER。显然,这样检测得到的MSER内部灰度值是小于边界的,想象一副黑色背景白色区域的图片,显然这个区域是检测不到的。因此对原图进行一次MSER检测后需要将其反转,再做一次MSER检测,两次操作又称MSER+和MSER-

2. Swt

也是主要用于文本检测,名称为笔画宽度变换检测。 流程首先计算图像的canny 边缘,然后根据边缘的方向信息计算图像的SWT ,根据笔画宽度信息将像素聚集成连通域,利用几何推理如连通域的高宽比,连通域笔画的方差,均值,中值等来过滤连通域,将连通域聚集成文本行,最后将文本行分割成一个个词。流程的核心为SWT和滤除连通域。

(可以参考https://www.cnblogs.com/dawnminghuang/p/3807678.html

 

 

13. 图像分割(Grabcut)

图像分割大致了解下,看着一篇博客就够了,其中也讲到了Grabcut——

图像分割技术介绍https://blog.csdn.net/SIGAI_CSDN/article/details/84024925

如果更全面对图像分割的算法进行分类,请看—— 

图像处理--图像分割算法介绍https://blog.csdn.net/yangleo1987/article/details/53173753

现在的图像分割离不开深度学习,请看深度学习之图像分割综述——

图像分割综述【深度学习方法】https://blog.csdn.net/weixin_41923961/article/details/80946586

都比较长,学习很花时间额。 

 

 

14. 目标跟踪(相关滤波KCF)

 

高速跟踪方法KCF主要出自2015的一篇PAMI的文章,请看下面的博客—— 

KCF目标跟踪方法分析与总结 http://www.cnblogs.com/YiXiaoZhou/p/5925019.html




写博不易,您的支持让知识之花绽放得更美丽  ^_^

 

2016-09-12 19:06:21 a987860319 阅读数 304
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14060 人正在学习 去看看 戴振良

前言

9-patch图像是一种特殊格式的文件,因此Android知道图像的哪些部分可以拉伸缩放,哪些部分不可以。经适当处理后,可保证背景图的边角与工具创建的图像保持一致性。
为什么要叫做9-patch呢?9-patch可将图像分成3×3的网格,即由9部分或9 patch组成的网格。网格角落的patch不会被缩放,边缘部分的4个patch只按一个维度缩放,而中间部分则同时按两个维度缩放,如下图所示:
这里写图片描述

一个列子

左边那条黑色线代表图片垂直拉伸的区域,上边的那条黑色线代表水平拉伸区域;那么底部以及右边框又要如何处理呢?它们定义了用于9-patch图像的可选drawable区域。drawable区域是内容(通常是文字)绘制的地方。如不引用drawable区域,则默认与可拉伸区域保持一致。
这里写图片描述

2019-07-31 23:16:58 wills798 阅读数 2210
  • NinePatch图片制作从入门到精通

    在Android应用开发当中经常需要使用图片,而图片又经常需要进行拉申,图片拉申的时候会出现变形,制作NinePatch图片可以指定图片的拉申区域,使图片拉申后还能获得比较好的效果。

    14060 人正在学习 去看看 戴振良

在阅读基于深度卷积神经网络的图像识别、分类或检测的文献时经常看到“patch”,不是很能理解,后来就总结了一下。

 

通过阅读,“patch”似乎是CNN输入图像的其中一小块,但它究竟是什么呢?当使用CNN解决问题时,“patch”什么时候开始起作用?为什么我们需要“patch”? “patch”和内核(即特征检测器)之间有什么关系?

在CNN学习训练过程中,不是一次来处理一整张图片,而是先将图片划分为多个小的块,内核 kernel (或过滤器或特征检测器)每次只查看图像的一个块,这一个小块就称为 patch,然后过滤器移动到图像的另一个patch,以此类推。

当将CNN过滤器应用到图像时,它会一次查看一个 patch 。

CNN内核/过滤器一次只处理一个 patch,而不是整个图像。这是因为我们希望过滤器处理图像的小块以便检测特征(边缘等)。这也有一个很好的正则化属性,因为我们估计的参数数量较少,而且这些参数必须在每个图像的许多区域以及所有其他训练图像的许多区域都是“好”的。

所以 patch 就是内核 kernel 的输入。这时内核的大小便是 patch 的大小。

如图,主动脉弓和心脏,绿色部分相同,而黄色部分不同。传统的CNN算法,区分效果不佳。在 Multi-Instance Multi-Stage Deep Learning for Medical Image Recognition 这篇文章中,作者针对这种场景提出了解决方法。

这样训练出的网络,就会对有区分度的patch敏感,而对无区分度的无感。

 

一个CNN层生成一个中间表示。该表示被传递到下一层。如果下一层是CNN,则应用完全相同的“patch”概念,并以完全相同的方式进行计算,即使中间表示不是您或我可以识别为“图像”的东西。

Python处理图像

阅读数 722

没有更多推荐了,返回首页