不使用库函数实现形态学处理(腐蚀和膨胀)

32 篇文章 1 订阅
13 篇文章 1 订阅

        这个小程序并不是完全不使用opencv库函数的,图像输入和显示等基本功能还是要使用库函数的。主要实现的是不使用库函数实现腐蚀和膨胀这两个问题。主要的目的是学习它的算法和提高C++代码编程能力。

整个程序包含macro.h,Morp.h,Morp.cpp和main.cpp四个文件:

1. macro.h:程序的宏定义,支持的腐蚀和膨胀的类型

#define SQUARE 0    //正方形
#define DISK   1    //圆形

2. Morp.h:类的声明

#include"opencv2/core/core.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/highgui/highgui.hpp"

#include"macro.h"

using namespace cv;

class Morp{
public:
    Mat toContr(Mat &);  //对图像求反
    Mat toThreshold(const Mat &,double); //阈值分割
    Mat toErode(Mat &,int type = SQUARE,int valve = 3);  //腐蚀函数,默认是0,默认结构元是3行3列的矩阵
    Mat toDilate(Mat &, int type = SQUARE, int valve = 3);  //膨胀函数,默认是0,默认结构元是3行3列的矩阵
    ~Morp();
private:
    void struElem(int, int);  //确定结构元类型
    Mat taskImage;  //待处理的图像
    int depth;      //图像的深度
    int **pElement;  //定义结构元
    int ele_row;    //结构元的行
    int ele_col;    //结构元的列
    int centre_x = 1;   //结构元的原点x
    int centre_y = 1;   //结构元的原点y
    double dist;       //结构元中(x,y)点到原点的距离
};

3. Morp.cpp:各个函数的定义

#include<iostream>
#include"Morp.h"
#include"opencv2/core/core.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/highgui/highgui.hpp"

#include"macro.h"

using namespace cv;
using namespace std;

Mat Morp::toContr(Mat &image)
{
    switch (image.depth()) //image.depth()返回的值是0~6的值,分别对应下列的值
    {
    case 0:
        depth = 255;
        break;
    default:
        break;
    }
    for (int i = 0; i < image.rows; i++)  
    {
        uchar* imageData = image.ptr<uchar>(i);
        for (int j = 0; j < image.cols; j++)
        {
            imageData[j] = depth - imageData[j];
        }
    }
    return image;
}

void Morp::struElem(int type, int value)
{
    switch (type)   //确定结构元的类型
    {
    case SQUARE:    //正方形
        if (value % 2 == 0)   //只处理奇数的value
            value++;
        pElement = new int*[value]; //定义动态二维数组,作为操作的结构元
        ele_row = value;
        ele_col = value;
        for (int i = 0; i < value; i++)
        {
            pElement[i] = new int[value];
        }
        centre_x = value / 2;
        centre_y = value / 2;
        for (int i = 0; i < value; i++)
        for (int j = 0; j < value; j++)
            pElement[i][j] = 1;
        break;
    case DISK:    //圆形
        pElement = new int*[2 * value + 1]; //定义动态二维数组,作为操作的结构元
        ele_row = 2 * value + 1;
        ele_col = 2 * value + 1;
        for (int i = 0; i < 2 * value + 1; i++)
        {
            pElement[i] = new int[2 * value + 1];
        }
        centre_x = value;
        centre_y = value;
        for (int i = 0; i < 2 * value + 1; i++)
        for (int j = 0; j < 2 * value + 1; j++)
        {
            dist = sqrt((centre_x - i)*(centre_x - i) + (centre_y - j)*(centre_y - j)); //求数组pElement点(i,j)到原点的距离
            if (dist <= value)
            {
                pElement[i][j] = 1;
            }
            else{
                pElement[i][j] = 0;
            }
        }
        break;
    default:
        cout << "输入参数不对,请从新输入!";
        break;
    }
}

Mat Morp::toThreshold(const Mat &image, double perc)
{
    taskImage.create(image.size(),image.type());
    switch (image.depth()) //image.depth()返回的值是0~6的值,分别对应下列的值
    {
    case 0:
        depth = 255;
        break;
    default:
        break;
    }
    double thresh = depth*perc;
    for (int i = 0; i < image.rows; i++)  //阈值操作,大于thresh为255,其他为0;
    {
        const uchar* imageData = image.ptr<uchar>(i);
        uchar* taskData = taskImage.ptr<uchar>(i);
        for (int j = 0; j < image.cols; j++)
        {
            if (imageData[j]>thresh)
            {
                taskData[j] = depth;
            }
            else{
                taskData[j] = 0;
            }
        }
    }
    return taskImage;
}

Mat Morp::toErode(Mat &image,int type,int value)
{
    struElem(type,value);    //确定结构元的类型及大小
    taskImage.create(image.rows+2*centre_y, image.cols+2*centre_x, image.type()); //创建一个比image图片大一个结构元的图片
    uchar* taskData = NULL;   //taskImage图像data指针
    uchar* imageData = NULL;  //image图像data指针
    for (int i = 0; i < taskImage.rows; i++) //将image图片复制到taskImage中,周围为0
    {
        taskData = taskImage.ptr<uchar>(i);
        if ((centre_y <= i) && (i < (taskImage.rows - centre_y)))
            imageData = image.ptr<uchar>(i - centre_y);
        for (int j = 0; j < taskImage.cols; j++)
        {
            if ((centre_y <= i) && (i < (taskImage.rows - centre_y))&&(centre_x <= j) && (j < (taskImage.cols - centre_x)))//在taskImage中判断出和image图片大小相同的中心区域
            {
                taskData[j] = imageData[j - centre_x];
            }
            else
            {
                taskData[j] = 0;
            }
        }
    }
    uchar **pChar = NULL;  //定义一个二维数组,以获得和结构元同行数的行首地址
    pChar = new uchar*[2 * centre_y + 1]; //使pChar和结构元具有相同的行
    int elStart_i = 0; //得到pChar数组行地址
    int elStart_j = 0; //得到pChar数组列地址
    for (int i = 0; i < taskImage.rows; i++)
    {
        if ((centre_y <= i) && (i < (taskImage.rows - centre_y)))//在taskImage中判断出和image图片大小相同的中心区域
        {
            imageData = image.ptr<uchar>(i - centre_y);
            elStart_i = i - centre_y;
            for (int k = 0; k < (2 * centre_y + 1); k++)
            {
                pChar[k] = taskImage.ptr<uchar>(elStart_i+k);
            }
        }
        for (int j = 0; j < taskImage.cols; j++)
        {
            if ((centre_y <= i) && (i < (taskImage.rows - centre_y)) && (centre_x <= j) && (j < (taskImage.cols - centre_x)))//在taskImage中判断出和image图片大小相同的中心区域
            {
                int containStru = 1;    //作为taskImage图片是否包含结构元判断依据
                elStart_i = i - centre_y;
                elStart_j = j - centre_x;
                for (int ii = 0; ii < ele_row;ii++)
                for (int jj = 0; jj < ele_col; jj++)
                {
                    if (pElement[ii][jj] == 1 && pChar[ii][elStart_j + jj] == 255) //只要有一个没有被包含,就对containStru操作
                    {
                        containStru = 0;
                    }
                }
                if (containStru == 1)
                {
                    imageData[j] = 0;
                }
                else{
                    imageData[j] = 255;
                }
            }
        }
    }
    delete[]pChar;
    return image;
}

Mat Morp::toDilate(Mat &image,int type, int value)
{
    struElem(type, value);    //确定结构元的类型及大小
    taskImage.create(image.rows + 2 * centre_y, image.cols + 2 * centre_x, image.type()); //创建一个比image图片大一个结构元的图片
    uchar* taskData = NULL;   //taskImage图像data指针
    uchar* imageData = NULL;  //image图像data指针
    for (int i = 0; i < taskImage.rows; i++) //将image图片复制到taskImage中,周围为0
    {
        taskData = taskImage.ptr<uchar>(i);
        if ((centre_y <= i) && (i < (taskImage.rows - centre_y)))
            imageData = image.ptr<uchar>(i - centre_y);
        for (int j = 0; j < taskImage.cols; j++)
        {
            if ((centre_y <= i) && (i < (taskImage.rows - centre_y)) && (centre_x <= j) && (j < (taskImage.cols - centre_x)))//在taskImage中判断出和image图片大小相同的中心区域
            {
                taskData[j] = imageData[j - centre_x];
            }
            else
            {
                taskData[j] = 0;
            }
        }
    }
    uchar **pChar = NULL;  //定义一个二维数组,以获得和结构元同行数的行首地址
    pChar = new uchar*[2 * centre_y + 1]; //使pChar和结构元具有相同的行
    int elStart_i = 0; //得到pChar数组行地址
    int elStart_j = 0; //得到pChar数组列地址
    for (int i = 0; i < taskImage.rows; i++)
    {
        if ((centre_y <= i) && (i < (taskImage.rows - centre_y)))//在taskImage中判断出和image图片大小相同的中心区域
        {
            imageData = image.ptr<uchar>(i - centre_y);
            elStart_i = i - centre_y;
            for (int k = 0; k < (2 * centre_y + 1); k++)
            {
                pChar[k] = taskImage.ptr<uchar>(elStart_i + k);
            }
        }
        for (int j = 0; j < taskImage.cols; j++)
        {
            if ((centre_y <= i) && (i < (taskImage.rows - centre_y)) && (centre_x <= j) && (j < (taskImage.cols - centre_x)))//在taskImage中判断出和image图片大小相同的中心区域
            {
                int containStru = 0;    //作为taskImage图片是否和结构元相加的依据
                elStart_i = i - centre_y;
                elStart_j = j - centre_x;
                for (int ii = 0; ii < ele_row; ii++)
                for (int jj = 0; jj < ele_col; jj++)
                {
                    if (pElement[ii][jj] == 1 && pChar[ii][elStart_j + jj] == 255) //只要有一个被包含,就对containStru操作
                    {
                        containStru = 1;
                    }
                }
                if (containStru == 1)
                {
                    imageData[j] = 255;
                }
                else{
                    imageData[j] = 0;
                }
            }
        }
    }
    delete[]pChar;
    return image;
}

Morp::~Morp()
{
    delete[]pElement;
}

4. main.cpp:程序的入口

#include<iostream>
#include"opencv2/core/core.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include"opencv2/highgui/highgui.hpp"

#include"Morp.h"
#include"macro.h"

using namespace cv;
using namespace std;

int main()
{
    Mat srcImage = imread("image/container-01.jpg");  //图片读入
    if (srcImage.empty())   //判断图片是否读入或用src.Image.data判断
    {
        cout << "图像加载不成功!";
        return 0;
    }
    //将srcImage灰度化
    Mat grayImage;
    cvtColor(srcImage,grayImage,COLOR_RGB2GRAY);
    
    //阈值操作
    Morp morp;
    Mat mImage;
    mImage = morp.toThreshold(grayImage,0.4);
    mImage = morp.toContr(mImage);  //求反
    imshow("求反", mImage);

    //腐蚀
    mImage= morp.toErode(mImage,DISK,4);
    imshow("腐蚀", mImage);

    //膨胀
    mImage = morp.toDilate(mImage, DISK, 4);
    imshow("膨胀", mImage);

    waitKey();
    return 0;

}

注:如果mImage= morp.toErode(mImage,DISK,5);和mImage= morp.toErode(mImage,SQUARE,10);会报错的,小于就不会!

        通过后续的学习,现在知道这个程序为什么会错,怎么改才能运行不报错了。另外一篇内存问题可以看这里

        这个程序之所以会错是因为对成员变量int **pElement操作之前没有对它进行内存检测。

mImage= morp.toErode(mImage,DISK,5);
mImage= morp.toErode(mImage,SQUARE,10);

在运行上面的第一行代码时,变量pElement会指向一个5*5的矩阵;当运行上面的第二行时,变量pElement并没有delete[]就又申请了10*10的内存块,所以这个程序最后回收内存时会报错。并不是上面“注”写的那样,运行下面

mImage= morp.toErode(mImage,DISK,4);
mImage= morp.toErode(mImage,DISK,4);

代码时,可能使用相同的内存块导致程序不崩溃。如果要程序不奔溃,也可以只让一个对象使用一次。如下:

Morp m1,m2;

mImage= m1.toErode(mImage,DISK,4);
mImage= m2.toErode(mImage,DISK,10);

        初步的修改方案是在使用成员变量int **pElement时,查看它是否分配了内存,如果有就delete[] pElement。可以把这个if语句放到void Morp::struElem(int type, int value)成员函数前面,其他的不变。

        修改后。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值