像素是图画的根本元素,像素与像素之间存在着某些联系,理解像素间的根本联系是数字图画处理的基础。常见的像素间的根本联系包含:邻域、邻接、通路、连通、间隔。

1. 邻域

邻域表明了像素之间的衔接联系。

像素(x,y)的邻域,是指与像素(x,y)对应的点的调集{(x+p,y+q)} ,其中 (p,q) 为一对有意义的整数。邻域是像素(x,y)附近像素形成的区域,像素 (x,y) 也被称为中心像素。

最常用的邻域有以下几种:

  • 4 邻域:关于像素(x,y),上下左右4个像素被称为 4 邻域,运用N4(p)N_4(p)表明。4 邻域的四个像素分别是:(x,y-1)、(x,y+1)、(x-1,y)、(x+1,y)。

  • D 邻域:关于像素(x, y), 其左上、右上、左下、右下的四个对角上的像素组成了 D 邻域,运用Nd(p)N_d(p)表明。D 邻域四个像素分别是:(x + 1, y + 1)、( x + 1, y – 1)、(x – 1, y + 1)、(x – 1, y – 1)。

  • 8 邻域:关于像素(x,y),它的 4 邻域的点和 D 邻域的点组成了 8 邻域,运用N8(p)N_8(p)表明。那么,N8(p)=N4(p)+Nd(p)N_8(p)=N_4(p)+N_d(p)

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

邻域是一个很基础的概念。后续咱们对图画进行卷积操作的时分,通常是对当时像素的邻域像素进行操作的

以一个最简略的均值滤波为例,均值滤波是关于每一个像素点, 将其设定为取其邻域窗口内的一切像素的平均值。

算术均值滤波器的公式:

g(x,y)=1m∗n∑i,j∈Sxyf(i,j)g(x,y)=frac{1}{m*n}sum_{i,jinS_{xy}}f(i,j)

其中,SxyS_{xy}表明以像素(x,y)为中心的区域,m*n 是 模板 的巨细。f(x,y) 表明原图画,g(x,y) 表明运用 SxyS_{xy} 定义的邻域中的像素所核算出的算术平均值。

这里的模板,也能够被称为核(kernels)、窗口(windows)、掩模(mask)。

下图以 3*3 的模板为例,均值滤波会对原图画的每一个像素点,核算它的邻域像素和模版矩阵的对应元素的乘积,然后加起来,作为该像素方位的值。窗口的移动是从左到右,然后从上到下顺次移动。

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

下面,完成一个简略的均值滤波函数

Mat meanFilter(Mat &src, int ksize = 3)
{
    cv::Mat dst = src.clone();
    int k0 = ksize/2;
    int sum[3] = {0,0,0};
    for(int i=k0;i<dst.rows-k0-1;i++)
    {
        for(int j=k0;j<dst.cols-k0-1;j++)
        {
            memset(sum,0, sizeof(sum));
            for(int channel = 0; channel<3; channel++)
            {
                for(int m = 0;m<ksize;m++)
                {
                    for (int n=0;n<ksize;n++)
                    {
                        sum[channel] += src.at<cv::Vec3b>(i-k0+m,j-k0+n)[channel];
                    }
                }
                dst.at<Vec3b>(i,j)[channel] = saturate_cast<uchar>((float)sum[channel] /(ksize*ksize));
            }
        }
    }
    return dst;
}

当然这个代码只是粗略地完成均值滤波,存在着许多优化的空间,例如运用积分图、卷积核别离等。OpenCV 也提供了均值滤波函数 blur() 函数。

int main(int argc,char *argv[])
{
    Mat src = imread(".../flower.jpg");
    imshow("src",src);
    Mat dst;
    dst = meanFilter(src, 15);
    imshow("meanFilter",dst);
    blur(src,dst,Size(15,15));
    imshow("blur",dst);
    waitKey(0);
    return 0;
}

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

上面只是简略例举了范畴的运用场景,后续会有专门的文章来详细介绍卷积和滤波。

2. 邻接

邻接是指两个像素,在方位上相邻而且取值相同或相近

咱们用 V 表明定义邻接的灰度值调集。在二值图画中,V={1} 表明值为1的像素邻接。在灰度图画中,V 包含更多的元素。

  • **4 邻接:**关于灰度值在 V 调集中的像素 p 和 q,假如 q 在 N4(p)N_4(p) 中,那么像素 p 和 q 是 4 邻接的。

  • **8 邻接:**关于灰度值在 V 调集中的像素 p 和 q,假如 q 在 N8(p)N_8(p) 中,那么像素 p 和 q 是 8 邻接的。

  • **m 邻接(混合邻接):**m 邻接是 8 邻接的改进。只需满足以下任何一个条件即可:

    • q 在 N4(p)N_4(p)

    • q 在 Nd(p)N_d(p) 中,且调集在 N4(p)∩N4(q)N_4(p)cap N_4(q) 中没有来自 V 中的像素。

像素 p 和 q 是 4 邻接,那么它们一定是 8 邻接的。反之,不一定成立。

下图反响了 8 邻接会带来二义性。

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

从图中能够看到,p 是中心像素。

  1. q1、q2 和 p 是 8 邻接的。

  2. q1 和 p 非 m 邻接的。

  3. q2 和 p 是 m 邻接的。

某条通路通过像素 q2、p、q1,那会有几种走法呢?

假如从 p、q1、q2 是 8 邻接的视点看,p 到 q1 能够有2种走法,所以 q2 到 q1 的通路有2条。

同理,从 m 邻接视点看,p 和 q1 只有1种走法,所以 q2 到 q1 的通路只有1条。

所以,m 邻接的引进是为了消除 8 邻接常常带来二义性

从调集的视点看:4邻接⊂m邻接⊂8邻接4 邻接subsetm 邻接subset8邻接

3. 通路

通路:从像素 p(x0,y0)(x_0,y_0) 到像素 q(xn,yn)(x_n,y_n) 的通路是特定的像素序列,其坐标为:

(x0,y0),(x1,y1),(x2,y2),…(xn,yn)(x_0,y_0),(x_1,y_1),(x_2,y_2),…(x_n,y_n)

而且满足, (xi,yi)(x_i,y_i)(xi−1,yi−1)(x_{i-1},y_{i-1}) 关于 1≤i≤n1leqileqn 是邻接的。

**闭合通路:**假如满足(x0,y0)=(xn,yn)(x_0,y_0)=(x_n,y_n),则通路是闭合通路。

由不同的邻接定义,能够得到不同的通路:4 邻接 => 4 通路,8 邻接 => 8 通路,m 邻接 => m 通路

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

所以,从中心的图能够看到 q2 和 q1 之间存在 8 通路,从最右的图能够看到 q2 和 q1 之间存在 m 通路。

从调集的视点看:4通路⊂m通路⊂8通路4通路subsetm 通路subset8通路

下图中,p-q 通路对应的是不同的通路。

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

4. 连通

连通:若 S 是图画中的一个像素子集,关于恣意的 p、q∈Sp、qinS。假如存在一条由 S 中像素组成的从 p 到 q 的通路,则称 p 在图画集 S 中与 q 连通

邻接连通的一种特例,连通是由一系列顺次邻接的像素组成的。

连通分为 4 连通8 连通

连通重量:关于 S 中恣意像素 p,一切与 p 相连通且又在 S 中的像素调集。

连通集:假如 S 中仅有一个连通重量,则 S 称为连通集。

在之前根本图形的绘制那篇文章里, 曾介绍过绘图函数所运用的 lineType 参数。

下面对这个参数做一些弥补阐明:

  • LINE_4 :根据 4 连通 Bresenham 算法处理的直线。

  • LINE_8 :根据 8 连通 Bresenham 算法处理的直线。

  • LINE_AA :根据高斯滤波滑润处理的直线。

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

下面的例子,展示了运用不同的 lineType 参数的作用

int main(int argc,char *argv[])
{
    Mat image = Mat::zeros(Size(80, 80), CV_8UC3);
    image.setTo(255);// 设置屏幕为白色
    Point p1(20, 0);
    Point p2(80, 60);
    Point p3(0, 0);
    Point p4(80, 80);
    Point p5(0, 20);
    Point p6(60, 80);
    line(image, p1, p2, Scalar(0, 0, 255), 1, LINE_4);
    line(image, p3, p4, Scalar(255, 0, 0), 1, LINE_8);
    line(image, p5, p6, Scalar(0, 255, 0), 1, LINE_AA);
    imshow("src", image);
    waitKey(0);
    return 0;
}

将生成的图片扩大,能够看到运用 LINE_4、LINE_8、LINE_AA 画出来的线段作用是不同的。运用 LINE_AA 作用看上去是最好的,其次是 LINE_8。

OpenCV 笔记(6):像素间的根本联系——邻域、邻接、通路、连通、间隔

通过邻接能够引申许多概念,邻接 -> 通路 -> 连通 -> 连通集 -> 区域/邻接区域 -> 远景和背景 -> 鸿沟

5. 间隔

关于像素 p(x,y)、q(s,t) 和 z(u,v),假如满足:

  • 非负性:D(p,q) ≥ 0

  • 同一性:D(p,q)=0,当且仅当p=q时

  • 对称性:D(p,q) = D(q,p)

  • 直递性:D(p,z) ≤ D(p,q) + D(q,z)

则称 D 是间隔的衡量函数。

在欧几里得空间中,点(x1,x2,…,xn)(x_1,x_2,…,x_n)和点(y1,y2,…,yn)(y_1,y_2,…,y_n)之间的闵可夫斯基间隔

D(x,y)=(∑i=1n∣xi−yi∣p)1/pD(x,y)=(sum_{i=1}^{n}|x_i – y_i|^p)^{1/p}

  • 曼哈顿间隔

当 p = 1 时,即为曼哈顿间隔或城市间隔、街区间隔,是指两个向量之间的间隔,在核算间隔时不触及对角线移动。像素 p(x,y) 和 q(s,t) 之间的间隔公式:

D4(p,q)=∣x−s∣+∣y−t∣D_4(p,q)=|x-s|+|y-t|

表明从像素 p 向像素 q 动身,每次能走的点必须是在当时像素点的 4 邻域中。一步一步走到 q 点后,总共通过的像素点数便是曼哈顿间隔。

  • 欧式间隔

当 p = 2 时,即为欧式间隔,便是直角坐标系的间隔。像素 p(x,y) 和 q(s,t) 之间的间隔公式:

De(p,q)=(x−s)2+(y−t)2D_e(p,q)=sqrt{(x-s)^2+(y-t)^2}

  • 切比雪夫间隔

当 p = ∞infty 时,即为切比雪夫间隔或棋盘间隔,像素 p(x,y) 和 q(s,t) 之间的间隔公式:

D8(x,y)=max(∣x−s∣,∣y−t∣)D_8(x,y)=max(|x-s|,|y-t|)

表明从像素 p 向像素 q 动身,每次能走的点必须是在当时像素点的 8 邻域中。一步一步走到 q 点后,总共通过的像素点数便是切比雪夫间隔。

6. 总结

本文触及到许多概念,这些概念代表着像素间的根本联系。像邻域、连通在后续文章中许多都会触及到,像间隔又跟类似度有关,所以它们是数字图画的基础。