数字图像处理总结(一)(二)

本文详细介绍了数字图像处理的基础知识,包括图像形成模型、DPI、灰度分辨率、像素邻域(4邻域、8邻域、D邻域、m邻域)以及图像连通域的内外边界。此外,还讨论了图像的距离度量和数学操作(加减乘除),并深入探讨了前向和后向变换、仿射变换等图像变换概念。
摘要由CSDN通过智能技术生成

Part1 Introduction

这部分没啥说的。

Part2 DIP基础

这部分主要介绍了成像的过程和数字图像的生成、表示和量化。还有数字图像的数据结构表示(二维数组)

简单图像形成模型

图像形成模型比较简单,主要由两部分组成,一个照射分量(illumination components)[0,∞],一个反射分量(reflectance components),[0,1]

在这里插入图片描述
在这里插入图片描述

dpi(dot per inch)

例如下图ipad的分辨率为1024*768,长宽是8,6inch,因此,dpi = 768/6 = 128

在这里插入图片描述

灰度分辨率

如何获得n-bits (1-8)的images?
两种方式:

  • 先右移位(8-n)位,再左移8-n位(只保留高2^n位);
  • 先右移位(8-n)位,再*255/(2^n-1)
c--:
byte[,] get_n_bits_img(byte[,] f,  int n)
{
	int w = f.GetLength(0);
	int h = f.GetLength(1);
	byte[,] g = new byte[w, h]; 
   
	for (int y=0;y<h;y++)
		for(int x=0;x<w;x++)
			{
				//g[x,y] = (byte)((f[x,y]>>(8-n))<<(8-n));
				g[x,y] = (byte)((f[x,y]>>(8-n))*255/((1<<n)));
			}
   
	return g;
}
void main()
{
	byte[,] f = LoadImg();
	ShowImg("f", f);
	
	for (int i=1;i<=8;i++)
		ShowImg("n="+i.ToString(), get_n_bits_img(f, i));

}

像素邻域

由这里主要包括以下几种邻域:4-adjacency、8-adjacency、m-adjacency、和D-adjacency。

4邻域

在这里插入图片描述

D邻域

在这里插入图片描述

8邻域(N4+ND)

在这里插入图片描述

m邻域

对于像素值为1的意思为前景,p和q均在前景中,m邻域意思是满足下面其中一个条件:

  1. 像素q在p的4邻域中;
  2. q在p的8邻域,但p和q的四邻域没有在前景的交集。

在这里插入图片描述
在这里插入图片描述

混合连接实质上是在像素间同时存在4-连接和8-连接时,优先采用4-连接,并屏蔽两个和同一像素间存在4-连接的像素之间的8-连接。

例如下面的例子,最右边是m邻域所绘制的连通,中间是8邻域。
在这里插入图片描述

c--:
bool Is_4adjacent(int px, int py, int qx, int qy)
{
	return Abs(px-qx)+Abs(py-qy)<=1
}

bool Is_8adjacent(int px, int py, int qx, int qy)
{
	return Abs(px-qx)+Abs(py-qy) < 2.1
	// return Is_4adjacent(px,py,qx,qy)||Is_D_adjacent(px,py,qx,qy)
}

bool Is_D_adjacent(int px, int py, int qx, int qy)
{
	return Abs(px-qx)+Abs(py-qy) == 2
}

图像连通域的内外边界

  • 内边界: 自身像素点是区域内的;四邻域有区域内的;(inner-border)
    在这里插入图片描述

  • 外边界:自身像素点是区域外的,八邻域有区域内的点;(outer-border)
    在这里插入图片描述

图像的距离度量

距离函数应首先满足如下的性质:(非负,对偶,三角)
在这里插入图片描述
本课程中老师主要讲了三种距离度量:

  • 欧式距离(euclidean distance)
    在这里插入图片描述

  • 城市街区块距离(city-block distance)

    x和y的差距和
    在这里插入图片描述

  • 棋盘格距离(chessboard distance)

    取 x和y上差距最大的

在这里插入图片描述
查看街区D4和棋盘D8距离的代码如下:
其中,D4>=De>=D8在这里插入图片描述

 byte[,] chessdistance(int x0, int y0, float r)
{
	
	byte[,] g = new byte[(int)(x0*2),(int)(y0*2)];
	for (int y=0;y<(int)(y0*2);y++)
		for (int x=0;x<(int)(x0*2);x++)
		{
			double temp = Abs(x-x0);
			if (temp<Abs(y-y0))
				temp = Abs(y-y0);
			if ( temp <= r )
				g[x,y] = 255;
		}
	return g;
}
byte[,] cityblockdistance(int x0, int y0, float r)
{
	
	byte[,] g = new byte[(int)(x0*2),(int)(y0*2)];
	for (int y=0;y<(int)(y0*2);y++)
		for (int x=0;x<(int)(x0*2);x++)
		{
			if ( (Abs(x-x0)+Abs(y-y0)) <= r )
				g[x,y] = 255;
		}
	return g;
}

void main()
{
	ShowImg("city", cityblockdistance(200, 200, 50));
	ShowImg("chess", chessdistance(200, 200, 50));
}

图像中的数学操作(加减乘除)

  • Rand() [0,1] 随机噪声

  • 多个噪声图像相加,用于图像的增强,方差减小;add

  • 两个图像相减,目标突出;sub

  • 两个图像相乘/除,ROI;mul div

  • 在这里插入图片描述

  • 算术运算的用处,归一化到0-255:
    在这里插入图片描述

  • 混合运算

在这里插入图片描述

  • 像素取反
  • 邻域操作(和后面的卷积类似)

图像中的变换

前向和后向变换

这里是(x,y) -> (u,v),后面仿射变换x,y和u,v是反的,注意区分!
在这里插入图片描述
图像处理中常用逆向映射,计算机图形学中常用前向映射。

仿射变换

在这里插入图片描述

下面的都是逆向变换,前向变换求一个逆就行了: 一般分析的时候先前向变换,然后再后向变换
先定义一下matrix和矩阵乘法函数

struct Matrix
{
   public float m11;
   public float m21;
   public float m31;

   public float m12;
   public float m22;
   public float m32;
}

byte[,] ST(byte[,] f,Matrix T)
{
   int w = f.GetLength(0);
   int h = f.GetLength(1);
 
   byte[,] g = new byte[w,h];

   for (int y=0;y<h;y++)
     for(int x=0;x<w;x++)
     {
         int u = (int)(x*T.m11+y*T.m21+T.m31);
         int v = (int)(x*T.m12+y*T.m22+T.m32);

         if (u>=0 && u<w && v>=0 && v<h) 
             g[u,v] = f[x,y];          
     }

   return g;
}
  • 垂直flip vertical
 Matrix createFlipV(int h)
{
   Matrix M = new Matrix();

   M.m11 = 1;        M.m12 = 0;
   M.m21 = 0;        M.m22 = -1;
   M.m31 = 0;        M.m32 = h-1; 

  return M;
}
  • 水平flip horizontal
 {
   Matrix M = new Matrix();

   M.m11 = -1;        M.m12 = 0;
   M.m21 = 0;         M.m22 = 1;
   M.m31 = w-1;       M.m32 = 0; 

  return M;
}
  • Scale
 Matrix createScale(float sx,float sy)
{
   Matrix M = new Matrix();

   M.m11 =1/sx;       M.m12 = 0;
   M.m21 = 0;         M.m22 =1/sy;
   M.m31 = 0;         M.m32 = 0; 

  return M;
}
  • ScaleAt
 Matrix createScaleAt(float x0,float y0,float k)
{
   Matrix M = new Matrix();

   M.m11 =1/k;       M.m12 = 0;
   M.m21 = 0;        M.m22 =1/k;
   M.m31 =x0-x0/k;   M.m32 = y0-y0/k; 

  return M;
}
  • 旋转

  • 平移

 Matrix createTranslation(float tx,float ty)
{
   Matrix M = new Matrix();

   M.m11 = 1;        M.m12 = 0;
   M.m21 = 0;        M.m22 = 1;
   M.m31 =-tx;      M.m32 =-ty; 

  return M;
}
  • skewX
Matrix createSkewX(float h,float w0)
{
   Matrix M = new Matrix();

   M.m11 =1;         M.m12 = 0;
   M.m21 =-w0/(h-1); M.m22 = 1;
   M.m31 = 0;        M.m32 = 0; 

  return M;
}

在这里插入图片描述

  • skewY
Matrix createSkewX(float h,float w0)
{
   Matrix M = new Matrix();

   M.m11 =1;         M.m12 = -h0/(w-1);
   M.m21 =0; M.m22 = 1;
   M.m31 = 0;        M.m32 = 0; 

  return M;
}

在这里插入图片描述
注意多重仿射变换要会(矩阵堆叠)

  • 饶图像中心点顺时针旋转90°和逆时针旋转90°(对于这类比较特殊的变换,可以使用多点观察法,求取转化矩阵)

方法(来自某彭姓大佬):

在这里插入图片描述
在这里插入图片描述

//下面的是前向映射的矩阵 分别是顺时针旋转90°和逆时针旋转90°
Matrix rotate90()
{
   Matrix M = new Matrix();

   M.m11 =0;          M.m12 = 1;
   M.m21 = -1;        M.m22 =0;
   M.m31 =255;        M.m32 = 0; 

  return M;
}

Matrix rotate_f90()
{
   Matrix M = new Matrix();

   M.m11 =0;         M.m12 = -1;
   M.m21 =1;         M.m22 =0;
   M.m31 =0;         M.m32 = 255; 

  return M;
}
  • 最近邻插值(注意0.5)
    在这里插入图片描述

  • 双线性变换
    在这里插入图片描述

struct Matrix
{
   public float m11;
   public float m21;
   public float m31;

   public float m12;
   public float m22;
   public float m32;
}

byte[,] ST(byte[,] f,Matrix T)
{
   int w = f.GetLength(0);
   int h = f.GetLength(1);
 
   byte[,] g = new byte[w,h];

   for (int y=0;y<h;y++)
     for(int x=0;x<w;x++)
     {
         int u = (int)(x*T.m11+y*T.m21+T.m31);
         int v = (int)(x*T.m12+y*T.m22+T.m32);

         if (u>=0 && u<w && v>=0 && v<h)
             g[x,y] = f[u,v];         
     }

   return g;
}

byte GetValueAt(byte[,]f,float x,float y)
{
    int i = (int)x;
    int j = (int)y;

    double a = x-i;
    double b = y-j;

    double fxj = f[i,j]*(1-a)+f[i+1,j]*a;
    double fxj1 = f[i,j+1]*(1-a)+f[i+1,j+1]*a;

    double fxy = fxj*(1-b)+fxj1*b;

    /*
    float f00 = f[i,j];
    float f10 = f[i+1,j];
    float f01 = f[i,j+1];
    float f11 = f[i+1,j+1];

    float ffxy = (f00*(1-a)+f10*a)*(1-b)+(f10*(1-a)+f11*a)*b;
    */

    return (byte)fxy;
}

byte[,] ST_BI(byte[,] f,Matrix T)
{
   int w = f.GetLength(0);
   int h = f.GetLength(1);
 
   byte[,] g = new byte[w,h];

   for (int y=0;y<h;y++)
     for(int x=0;x<w;x++)
     {
         float u = x*T.m11+y*T.m21+T.m31;
         float v = x*T.m12+y*T.m22+T.m32;

         if (u>=0 && u<w-2 && v>=0 && v<h-2)
             g[x,y] = GetValueAt(f,u,v);         
     }

   return g;
}


Matrix createFlipV(int h)
{
   Matrix M = new Matrix();

   M.m11 = 1;        M.m12 = 0;
   M.m21 = 0;        M.m22 = -1;
   M.m31 = 0;        M.m32 = h-1; 

  return M;
}

Matrix createFlipH(int w)
{
   Matrix M = new Matrix();

   M.m11 = -1;        M.m12 = 0;
   M.m21 = 0;         M.m22 = 1;
   M.m31 = w-1;       M.m32 = 0; 

  return M;
}

Matrix createTranslation(float tx,float ty)
{
   Matrix M = new Matrix();

   M.m11 = 1;        M.m12 = 0;
   M.m21 = 0;        M.m22 = 1;
   M.m31 =-tx;      M.m32 =-ty; 

  return M;
}

Matrix createScale(float sx,float sy)
{
   Matrix M = new Matrix();

   M.m11 =1/sx;       M.m12 = 0;
   M.m21 = 0;         M.m22 =1/sy;
   M.m31 = 0;         M.m32 = 0; 

  return M;
}


Matrix createScaleAt(float x0,float y0,float k)
{
   Matrix M = new Matrix();

   M.m11 =1/k;       M.m12 = 0;
   M.m21 = 0;        M.m22 =1/k;
   M.m31 =x0-x0/k;   M.m32 = y0-y0/k; 

  return M;
}


void main()
{
   byte[,] f = LoadImg();
   ShowImg("f",f);

   int w = f.GetLength(0);
   int h = f.GetLength(1);
   
   // ShowImg("FlipV",ST(f,createFlipV(h)));
   // ShowImg("FlipH",ST(f,createFlipH(w)));
  // ShowImg("Translate",ST(f,createTranslation(40,80)));

//   ShowImg("Scale 0.5",ST(f,createScale(0.5f,0.5f)));
//   ShowImg("Scale 2",ST(f,createScale(2,2)));
//   ShowImg("Scale 0.5",ST(f,createScale(0.5f,0.75f)));

   ShowImg("Scale At",ST(f,createScaleAt(w/2,h/2,8)));
   ShowImg("Scale At",ST_BI(f,createScaleAt(w/2,h/2,8)));
}
  • 配准(简单平移)
    在这里插入图片描述
 FM
(0,0)  => x00,y00
(w-1,0)  => x10,y10
(0,h-1)  => x01,y01
(w-1,h-1) => x11,y11
*/
0,0,    512,80,     80,512,     512+80,    512+80,      512,512
// IM
byte[,] img_reg(byte[,]f,float x00,float y00,     float x10,float y10,     float x01,float y01,     float x11,float y11,     int w,int h)
{

   int iw = f.GetLength(0);
   int ih = f.GetLength(1);


   byte[,] g = new byte[w,h];

   for (int y=0;y<h;y++)
     for(int x=0;x<w;x++)
     {
         float a = (float)x/(w-1);
         float b = (float)y/(h-1);
	/*
	    	float f00 = f[i,j];
    		float f10 = f[i+1,j];
    		float f01 = f[i,j+1];
    		float f11 = f[i+1,j+1];

    		float ffxy = (f00*(1-a)+f10*a)*(1-b)+(f10*(1-a)+f11*a)*b;
	*/	

        
         int u = (int)((x00*(1-a)+x10*a)*(1-b)+(x01*(1-a)+x11*a)*b);
         int v = (int)((y00*(1-a)+y10*a)*(1-b)+(y01*(1-a)+y11*a)*b);

// if (x=0,y=0)      u = x00, v = y00
// if (x=w-1,y=0)    a = 1, b =0 u = x10, v= y10, 

         if (u>=0 && u<iw && v>=0 && v<ih)
             g[x,y] = f[u,v]; 
         else g[x,y] = 255;        
     }


  return g; 
}
  • 图像某点 Scale (V2更加自然)
byte[,] ZoomAt(byte[,] f,float x0,float y0,float k)   // 直接向量计算就好了
{
   int w = f.GetLength(0);
   int h = f.GetLength(1);
 
   byte[,] g = new byte[w,h];

   for (int y=0;y<h;y++)
     for(int x=0;x<w;x++)
     {
         int u = (int)((x-x0)/k+x0);
         int v = (int)((y-y0)/k+y0);

         if (u>=0 && u<w && v>=0 && v<h)
           g[x,y] = f[u,v];         
     }

   return g;
}
byte[,] localZooming(byte[,] f,float x0,float y0,float R,float k)  // 局部放大,在原来的基础上,限制在一个圆里面,但不够自然
{
   int w = f.GetLength(0);
   int h = f.GetLength(1);
 
   byte[,] g = new byte[w,h];

   for (int y=0;y<h;y++)
     for(int x=0;x<w;x++)
     {
         int u = (int)((x-x0)/k+x0);
         int v = (int)((y-y0)/k+y0);

         if ((x-x0)*(x-x0)+(y-y0)*(y-y0)<=R*R)
              g[x,y] = f[u,v];
         else g[x,y] = f[x,y];

         /*
         if (u>=0 && u<w && v>=0 && v<h)
           g[x,y] = f[u,v];         
         */
     }

   return g;
}
byte[,] localZoomingV2(byte[,] f,float x0,float y0,float R,float K)  
// 局部放大,在原来的基础上,限制在一个圆里面,
// 然后距离圆越远的,放大倍数越小
// 这样看起来比较自然
{
   int w = f.GetLength(0);
   int h = f.GetLength(1);
 
   byte[,] g = new byte[w,h];

   for (int y=0;y<h;y++)
     for(int x=0;x<w;x++)
     {
         double d = Sqrt((x-x0)*(x-x0)+(y-y0)*(y-y0));
         double t = d/R;

         if (t>1) g[x,y] = f[x,y];
         else 
         {
           double k = K*(1-t)+t;
 
           int u = (int)((x-x0)/k+x0);
           int v = (int)((y-y0)/k+y0);
           if (u>=0 && u<w && v>=0 && v<h)
             g[x,y] = f[u,v];         
         }
     }

   return g;
}
void main()
{
   byte[,] f = LoadImg();
   ShowImg("f",f);

//   ShowImg("Zoom At",localZooming(f,269,262,64,2));
//   ShowImg("Zoom At",localZooming(f,269,262,64,0.5f));

   ShowImg("Zoom At",localZoomingV2(f,269,262,64,2));
   ShowImg("Zoom At",localZoomingV2(f,269,262,64,0.5f));

}

主要参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

百年后封笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值