表示和描述

Posted zizi7

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了表示和描述相关的知识,希望对你有一定的参考价值。

接上篇 表示和描述(1)


边界描述

主要讨论边界的形状数描述、傅里叶描述和其他简单的描述。

1)基础描述

边界长度:边界的像素数粗略近似

边界直径:相隔最远的两个点的距离(链码一节介绍的长短轴)

边界偏心率:长轴/短轴

其他规范化近似:外接圆、外接矩形、外接凸包


2)形状数描述

参考表示和描述(1)的Freeman编码,图1-2形状编号的长度即为形状数的阶。

这个概念的意义在于,你可以指定一个边界的阶(以4-方向为例,对于闭合边界,其阶为大于等于4的偶数),然后对边界做重采样。

如图1-2中指定形状数阶数为18,首先获得其基本矩形(左2),然后计算其最接近18的划分(6*3,左3)。

在轮廓匹配中,可以用形状数作为参考:以4-方向为例,依次取阶数K=4,6,8...2n(显然随着K的增大,边界描述越精确),对待匹配的两组边界生成形状数,比较两组形状数,取d为使得两者相同的最大阶。显然对于相同的两条边界,d=无穷大。


3)傅里叶描述

使用一个复数来描述一个点:s(k) = x(k) + jy(k),然后对其做离散傅里叶变换:

这里介绍频域的4种边界操作(图2-1):一种是通过忽略部分高频傅里叶系数获得边界的近似描述(低频决定边界形状,高频决定边界细节);另三种是旋转、平移、缩放。

a. 边界近似

取前P个傅里叶系数做逆变换:。方便起见,直接将后K-P个系数赋0做逆变换就可以了。

b. 边界旋转


需要注意的是这里的旋转是以序列中(0,0)为原点的,将序列减去质心就可以实现以质心为原点旋转了。

c. 边界平移

,其中,乘以冲击序列的傅里叶变换

d. 边界缩放

,其中a为缩放系数


图2-1. 边界傅里叶描述

如图2-1所示,a为原始图像,b~e为使用Moore边界追踪算法后在频域做平移、旋转、缩放、近似。其中旋转部分做了中心平移,近似部分取前2/3的傅里叶系数做逆变换。

此外,边界还可以用统计矩来描述,但一般对区域的不变矩描述更广泛。


区域描述

主要讨论区域的纹理描述、不变矩描述和其他简单描述

1)基础描述

区域面积、周长都是用相应像素数来粗略估计

区域致密性:(周长)^2 / 面积。显然圆形具有最小的致密性。

区域圆度率:区域面积 / 具有相同周长的圆的面积。


2)纹理描述

纹理描述主要有三种方法:统计方法、结构方法、频谱方法。

统计方法考察纹理的平滑、粗糙、粒状等特征;结构方法考察纹理的排列描述;频谱方法考察纹理的周期性。

以下主要讨论统计方法的几种指标

a. 统计矩

对一个区域,统计其归一直方图,计算其均值的z的第n阶矩为,其中m是平均灰度

n=2:二阶矩(方差),描述区域的灰度对比变化。由其构成的归一化R度量对粗糙的区域有较大响应

n=3、4:三阶矩度量直方图倾斜性(负数表示总体小于均值),四阶矩度量直方图平坦度。

此外还有个重要的度量:直方图,描述区域的信息量。


b. 灰度共生矩阵(GLCM)

GLCM不仅考察灰度分布,还考虑了像素的相对位置。

令Q为两个像素相对位置的一个定义(比如当前像素的右邻像素),G为针对区域f由Q生成的共生矩阵(对于8bit图像,大小为256*256)。按当前Q定义,g_ij表示区域f中灰度值为i且其右邻像素灰度值为j的像素对数目(图3-1)。


图3-1. 灰度共生矩阵(《数字图像处理》)

,由GLCM可得到以下统计量

对比度:,纹理越深,该值越大

相关性,其中。邻域变化越小,相关性值越大。

能量:,能量越大表示当前纹理越规则

熵:,熵越大表示当前纹理越复杂


3)不变矩描述

令图像f(x,y)尺寸M*N,其二维(p+q)阶为,相应的(p+q)阶中心矩为,其中

由以上定义获得其归一化中心矩:,其中

根据归一化中心矩可获得7个不变矩(hu不变矩),其对同一区域的平移、缩放、旋转、镜像能保持一致描述


OpenCV有hu不变矩的实现:

// 处理图像
    int i, j;
    Mat src = imread("../DIP_CODE+IMAGE/IMAGE/dipum_images_ch11/Fig1123(a)(Original_Padded_to_568_by_568).tif",0);
    
    Mat shift_(src.size(), CV_8UC1, Scalar::all(0));     // 平移
    for (i = 0; i < src.rows-50; i++)
    {
        uchar* ptrSrc = src.ptr<uchar>(i);
        uchar* ptrShift = shift_.ptr<uchar>(i + 50);
        for (j = 0; j < src.cols-50; j++)
        {
            uchar value = ptrSrc[j];
            if (value > 0)
                ptrShift[j+50] = value;
        }
    }

    Mat resize_(src.size(), CV_8UC1, Scalar::all(0));  // 缩放
    Mat resize_tmp;
    resize(src, resize_tmp, Size(src.cols/2, src.rows/2));
    Mat roi_(resize_, Rect(src.cols / 4, src.rows / 4, src.cols / 2, src.rows / 2));
    resize_tmp.copyTo(roi_);

    Mat flip_;                                                          // 镜像
    flip(src, flip_, 1);

    Mat rotate_;                                                     // 旋转
    Point2f center_(src.cols/2, src.rows/2);
    Mat r = getRotationMatrix2D(center_, 45, 1);
    warpAffine(src, rotate_, r, src.size());

    namedWindow("src");
    namedWindow("shift");
    namedWindow("resize");
    namedWindow("flip");
    namedWindow("rotate");
    imshow("src", src);
    imshow("shift", shift_);
    imshow("resize", resize_);
    imshow("flip", flip_);
    imshow("rotate", rotate_);

    
	// 统计不变矩
    Moments mom = moments(src);
    double hu[7];
    HuMoments(mom, hu);
    for (i = 0; i < 7; i++)
        hu[i] = log(abs(hu[i])) * (-1*hu[i]/abs(hu[i]));
    cout << "************** src *****************" << endl;
    for (i = 0; i < 7; i++)
        cout << "hu[" << i << "]=" << hu[i] << "; ";
    cout << endl;

    mom = moments(shift_);
    HuMoments(mom, hu);
    for (i = 0; i < 7; i++)
        hu[i] = log(abs(hu[i])) * (-1*hu[i] / abs(hu[i]));
    cout << "************** shift *****************" << endl;
    for (i = 0; i < 7; i++)
        cout << "hu[" << i << "]=" << hu[i] << "; ";
    cout << endl;

    mom = moments(resize_);
    HuMoments(mom, hu);
    for (i = 0; i < 7; i++)
        hu[i] = log(abs(hu[i])) * (-1*hu[i] / abs(hu[i]));
    cout << "************** resize *****************" << endl;
    for (i = 0; i < 7; i++)
        cout << "hu[" << i << "]=" << hu[i] << "; ";
    cout << endl;

    mom = moments(flip_);
    HuMoments(mom, hu);
    for (i = 0; i < 7; i++)
        hu[i] = log(abs(hu[i])) * (-1*hu[i] / abs(hu[i]));
    cout << "************** flip *****************" << endl;
    for (i = 0; i < 7; i++)
        cout << "hu[" << i << "]=" << hu[i] << "; ";
    cout << endl;

    mom = moments(rotate_);
    HuMoments(mom, hu);
    for (i = 0; i < 7; i++)
        hu[i] = log(abs(hu[i])) * (-1*hu[i] / abs(hu[i]));
    cout << "************** rotate *****************" << endl;
    for (i = 0; i < 7; i++)
        cout << "hu[" << i << "]=" << hu[i] << "; ";
    cout << endl;

    waitKey(0);

接下篇 表示和描述(3)

以上是关于表示和描述的主要内容,如果未能解决你的问题,请参考以下文章

Alfred常见使用

如何从片段 KOTLIN 中调用意图 [重复]

2021-12-24:划分字母区间。 字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。 力扣763。某大厂面试

轻松保存重复多用的代码片段

这个片段中发生了啥关于 n 在 pc[i] 中的表示

片段和活动之间的核心区别是啥?哪些代码可以写成片段?