C++-掩膜外数据填补(边缘扩展法)

简介: C++-掩膜外数据填补(边缘扩展法)

场景需求

      当对数据进行处理时,可能会遇到一种问题:计算区间(掩膜)内的数据和计算区间(掩膜)外的数据之间,有个断层,区间外的数据默认均为0或者NaN值,而这些原本不处于计算区间的数据可能无意中对计算区间内的数据带来影响。


      举个例子:当用某个尺寸比如3*3大小的模板进行均值滤波处理时,掩膜边缘的滤波模板中存在处于掩膜外的数据,这些数据会引入很大的误差;又或者,在进行FFT(傅里叶变换)时,掩膜外的数据通常置0处理,那么掩膜边缘的数据会有很大的突变,这些都会使频域的信息受到影响,在反傅里叶变换后,引入较大的边缘高频误差或吉布斯噪声等等。


      为了解决上述问题,需要对掩膜外的数据进行一定的处理,比如让它们同掩膜内的数据一致,又或者通过最小二乘的方法进行数据拟合。本文提供了一种数据填补方法(边缘扩展法):先按照行再按照列,分别用最靠近的掩膜边缘元素,填补至该行或者列某一侧的掩膜外数据。这样操作能大大减少由数据突变引起的误差,属于一种比较简单的填补方法,实现起来很方便也便于理解。

功能函数代码

/**
* @brief FillMaskData                掩膜外数据填补函数
* @param phasein                     需要填补数据的数组
* @param mask                        同尺寸的掩膜
* @return                            填补后的数组
*/
//掩膜外数据填充:为了避免掩膜边界的值受0值影响导致异常
cv::Mat FillMaskData(cv::Mat phasein,cv::Mat mask)
{
  cv::Mat phase = phasein.clone();
  /********************************************************************
   * mask为0的区域,对phase进行行操作,用最靠近的元素替换掉其他元素
   *        0 0 0 0 0           0 1 2 3 4                     0 1 2 3 4
   *        0 0 1 0 0           5 6 7 8 9   行操作后          7 7 7 7 7
   * mask = 0 1 1 1 0 ,phase = 9 8 7 6 5   ========》phase = 8 8 7 6 6
   *        0 0 1 0 0           4 3 2 1 0                     2 2 2 2 2
   *        0 0 0 0 0           1 2 3 4 5                     1 2 3 4 5
   ********************************************************************/
  for (int i = 0; i != mask.rows; ++i) {
    int j = 0;
    int jj = mask.cols - 1;
    const uchar* p = mask.ptr<uchar>(i);
    for (; j != mask.cols; j++, p++) {
      if (*p != 0)
        break;
    }
    p = mask.ptr<uchar>(i) + jj;
    for (; jj != -1; jj--, p--) {
      if (*p != 0)
        break;
    }
    if (j > 0 && j < mask.cols)
      phase.row(i)(cv::Range::all(), cv::Range(0, j)).setTo(phase.at<float>(i, j));
    if (jj > -1 && jj < mask.cols - 1)
      phase.row(i)(cv::Range::all(), cv::Range(jj + 1, mask.cols)).setTo(phase.at<float>(i, jj));
  }
  /********************************************************************
   * mask为0的区域,对phase进行列操作,用最靠近的元素替换掉其他元素,
   *        0 0 0 0 0           0 1 2 3 4                     0 8 7 6 4
   *        0 0 1 0 0           7 7 7 7 7   列操作后          7 8 7 6 7
   * mask = 0 1 1 1 0 ,phase = 8 8 7 6 6   ========》phase = 8 8 7 6 6
   *        0 0 1 0 0           2 2 2 2 2                     2 8 2 6 2
   *        0 0 0 0 0           1 2 3 4 5                     1 8 2 6 5
   ********************************************************************/
  cv::Mat mask_t = mask.t();  //mask转置
  for (int i = 0; i != mask_t.rows; ++i) {
    int j = 0;
    int jj = mask_t.cols - 1;
    const uchar* p = mask_t.ptr<uchar>(i);
    for (; j != mask_t.cols; j++, p++) {
      if (*p != 0)
        break;
    }
    p = mask_t.ptr<uchar>(i) + jj;
    for (; jj != -1; jj--, p--) {
      if (*p != 0)
        break;
    }
    if (j > 0 && j < mask_t.cols)
      phase.col(i)(cv::Range(0, j), cv::Range::all()).setTo(phase.at<float>(j, i));
    if (jj > -1 && jj < mask_t.cols - 1)
      phase.col(i)(cv::Range(jj + 1, mask_t.cols), cv::Range::all()).setTo(phase.at<float>(jj, i));
  }
  return phase;
}

C++测试代码

#include<iostream>
#include<opencv2/opencv.hpp>
#include<ctime>
using namespace std;
using namespace cv;
cv::Mat FillMaskData(cv::Mat phasein, cv::Mat mask);
int main(void)
{
  cv::Mat src = cv::Mat::zeros(100, 100, CV_32FC1);
  cv::Mat mask = cv::Mat::zeros(100, 100, CV_8UC1);;
  cv::circle(mask, cv::Point(50, 50), 40, 255, -1);
    // 随机生成数据
  for (int i = 0; i < src.rows; ++i)
  {
    uchar *m = mask.ptr<uchar>(i);
    float *s = src.ptr<float>(i);
    for (int j = 0; j < src.cols; ++j)
    {
      if (m[j] == 255)
        s[j] = rand() % 255+20;
    }
  }
    // 因为该Mat是个float型的矩阵,除以255可以归一化数据,进而便于观察数据的灰度颜色
  src = src / 255;
  cv::Mat result = FillMaskData(src, mask);
  system("pause");
  return 0;
}
//掩膜外数据填充:为了避免掩膜边界的值受0值影响导致异常
cv::Mat FillMaskData(cv::Mat phasein,cv::Mat mask)
{
  cv::Mat phase = phasein.clone();
  /********************************************************************
   * mask为0的区域,对phase进行行操作,用最靠近的元素替换掉其他元素
   *        0 0 0 0 0           0 1 2 3 4                     0 1 2 3 4
   *        0 0 1 0 0           5 6 7 8 9   行操作后          7 7 7 7 7
   * mask = 0 1 1 1 0 ,phase = 9 8 7 6 5   ========》phase = 8 8 7 6 6
   *        0 0 1 0 0           4 3 2 1 0                     2 2 2 2 2
   *        0 0 0 0 0           1 2 3 4 5                     1 2 3 4 5
   ********************************************************************/
  for (int i = 0; i != mask.rows; ++i) {
    int j = 0;
    int jj = mask.cols - 1;
    const uchar* p = mask.ptr<uchar>(i);
    for (; j != mask.cols; j++, p++) {
      if (*p != 0)
        break;
    }
    p = mask.ptr<uchar>(i) + jj;
    for (; jj != -1; jj--, p--) {
      if (*p != 0)
        break;
    }
    if (j > 0 && j < mask.cols)
      phase.row(i)(cv::Range::all(), cv::Range(0, j)).setTo(phase.at<float>(i, j));
    if (jj > -1 && jj < mask.cols - 1)
      phase.row(i)(cv::Range::all(), cv::Range(jj + 1, mask.cols)).setTo(phase.at<float>(i, jj));
  }
  /********************************************************************
   * mask为0的区域,对phase进行列操作,用最靠近的元素替换掉其他元素,
   *        0 0 0 0 0           0 1 2 3 4                     0 8 7 6 4
   *        0 0 1 0 0           7 7 7 7 7   列操作后          7 8 7 6 7
   * mask = 0 1 1 1 0 ,phase = 8 8 7 6 6   ========》phase = 8 8 7 6 6
   *        0 0 1 0 0           2 2 2 2 2                     2 8 2 6 2
   *        0 0 0 0 0           1 2 3 4 5                     1 8 2 6 5
   ********************************************************************/
  cv::Mat mask_t = mask.t();  //mask转置
  for (int i = 0; i != mask_t.rows; ++i) {
    int j = 0;
    int jj = mask_t.cols - 1;
    const uchar* p = mask_t.ptr<uchar>(i);
    for (; j != mask_t.cols; j++, p++) {
      if (*p != 0)
        break;
    }
    p = mask_t.ptr<uchar>(i) + jj;
    for (; jj != -1; jj--, p--) {
      if (*p != 0)
        break;
    }
    if (j > 0 && j < mask_t.cols)
      phase.col(i)(cv::Range(0, j), cv::Range::all()).setTo(phase.at<float>(j, i));
    if (jj > -1 && jj < mask_t.cols - 1)
      phase.col(i)(cv::Range(jj + 1, mask_t.cols), cv::Range::all()).setTo(phase.at<float>(jj, i));
  }
  return phase;
}

测试效果

图1 原图

图2 掩膜

图3 填补后效果图

图3 填补后效果图

      填补后效果图虽然看起来很丑,但对数据处理而言还是很有益处的,四角的区域为黑色是因为没有对应的掩膜数据填充,也没什么影响,想改进下也是很简单的~


      本文是基于OpenCV实现的,用数组等数据结构或者Eigen库之类的也可,逻辑懂了,这些都只是工具而已~


      本文介绍的方法比较简单,大家有更好更高级的方法欢迎评论留言,一起学习进步!


      如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

相关文章
|
10天前
|
存储 C++ UED
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展
本文介绍了如何通过四步实现C++插件化编程,实现功能定制与扩展。主要内容包括引言、概述、需求分析、设计方案、详细设计、验证和总结。通过动态加载功能模块,实现软件的高度灵活性和可扩展性,支持快速定制和市场变化响应。具体步骤涉及配置文件构建、模块编译、动态库入口实现和主程序加载。验证部分展示了模块加载成功的日志和配置信息。总结中强调了插件化编程的优势及其在多个方面的应用。
118 60
|
5月前
|
存储 Serverless 数据安全/隐私保护
C++ 类的成员函数和数据成员的技术性探讨
C++ 类的成员函数和数据成员的技术性探讨
60 0
|
1月前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
2月前
|
存储 C++
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
【C++】C++ 基于QT实现散列表学生管理系统(源码+数据+课程论文)【独一无二】
|
2月前
|
存储 算法 C++
C++ STL应用宝典:高效处理数据的艺术与实战技巧大揭秘!
【8月更文挑战第22天】C++ STL(标准模板库)是一组高效的数据结构与算法集合,极大提升编程效率与代码可读性。它包括容器、迭代器、算法等组件。例如,统计文本中单词频率可用`std::map`和`std::ifstream`实现;对数据排序及找极值则可通过`std::vector`结合`std::sort`、`std::min/max_element`完成;而快速查找字符串则适合使用`std::set`配合其内置的`find`方法。这些示例展示了STL的强大功能,有助于编写简洁高效的代码。
40 2
|
2月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
2月前
|
存储 数据挖掘 C语言
【C/C++】C/C++车辆交通违章管理系统(源码+数据文件)【独一无二】
【C/C++】C/C++车辆交通违章管理系统(源码+数据文件)【独一无二】
|
2月前
|
存储 安全 数据处理
【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】
【C++】C++ 超市会员卡管理系统(面向对象)(源码+数据)【独一无二】
|
1月前
|
图形学 C++ C#
Unity插件开发全攻略:从零起步教你用C++扩展游戏功能,解锁Unity新玩法的详细步骤与实战技巧大公开
【8月更文挑战第31天】Unity 是一款功能强大的游戏开发引擎,支持多平台发布并拥有丰富的插件生态系统。本文介绍 Unity 插件开发基础,帮助读者从零开始编写自定义插件以扩展其功能。插件通常用 C++ 编写,通过 Mono C# 运行时调用,需在不同平台上编译。文中详细讲解了开发环境搭建、简单插件编写及在 Unity 中调用的方法,包括创建 C# 封装脚本和处理跨平台问题,助力开发者提升游戏开发效率。
107 0
|
3月前
|
存储 C++ 容器
开发与运维数组问题之C++标准库中提供数据容器作为数组的替代如何解决
开发与运维数组问题之C++标准库中提供数据容器作为数组的替代如何解决
50 5