数字图像处理——扫描件背景处理

数字图像处理实验


实验目的

去除如图所示图片背景(图片来源见水印):
数字图像处理——扫描件背景处理_第1张图片


第一次尝试

在网上查询资料后发现OpenCV有内置函cv::inpaint(),其功能主要是修复图像中的污损区域,修复效果较好,故尝试使用。

但是在仔细查阅使用文档后发现,该函数的使用需要一个掩膜才能对图像处理,而获取掩膜的难度大,不易实现,所以放弃了这种方法。

第二次尝试

在仔细看过待处理图片后,发现图片的待处理区域与其他区域对比度大,灰度值差距多,故采用阈值分割的方法对图片进行处理,并取得较好的结果。

  1. 获取全局阈值

    通过迭代的方法获得了全局阈值T1,并对三个通道分别处理,对阈值小于T1的不做处理(灰度值低,代表正常深色区域,保留);而对大于T1的像素值置为255(设置为白色)。三个通道做上述相同处理,代码及实现结果如下:

    Mat interatethreshold(Mat image)
    {
        Mat img = image.clone();
        vector<Mat>channels;
        split(img, channels);
        cout << "begin to find the threshold" << endl;
        for(int I = 0; I < 3; ++I)
        {
            double T1 = 0;
            double n1 = 0, count1 = 0, n2 = 0, count2 = 0;
            double T2 = 255; // 防止分母为0
    
            while(true)
            {
                for(int i = 0; i < (channels[I]).rows; ++i)
                {
                    for(int j = 0; j < (channels[I]).cols; ++j)
                    {
                        if(double((channels[I]).at<uchar>(i, j)) >= T1)
                        {
                            ++n1;
                           count1 += double((channels[I]).at<uchar>(i, j));  // 统计阈值两边的像素值和个数
                        }
                        else
                        {
                            ++n2;
                            count2 += double((channels[I]).at<uchar>(i, j));
                        }
                    }
                }
    
                double temp1 = count1 / n1;
                double temp2 = count2 / n2;
                T2 = (temp1 + temp2) / 2.0;
    
                if(abs(T2 - T1) <= 1e-3)    
                    break;
    
                else
                {
                    T1 = T2;
                }
    
            }
            for(int i = 0; i < (channels[I]).rows; ++i)
            {
                for(int j = 0; j < (channels[I]).cols; ++j)
                {
                    if((double)(channels[I]).at<uchar>(i, j) > T2)
                        (channels[I]).at<uchar>(i, j) = 255;
                }
            }
        }
    
        merge(channels, img);
        return img;
    }
    
数字图像处理——扫描件背景处理_第2张图片

可以看出效果已经非常好,达到了要求,但是仔细观察可发现图片中的横线不是很清晰,也有一些没有处理完的痕迹,所以进行第二步操作;

  1. 灰度线性变换

    利用灰度线性变换提高图片的清晰度,并将灰度范围提升至_[0, 255]_之间,实现代码及效果如下:

    Mat linearTransform(Mat image)
    {
        Mat image_1 = image.clone();
        //首先遍历图片,找出现在灰度值范围
        //首先检测是否是多通道图片
        //多通道图片需要分通道变换
        double channel = image_1.channels();
        int rows = image.rows,  cols = image.cols;
    
        //如果是单通道图片
        if(image.channels()==1)
        {
            double max = 0;
            double min = 255;
            double c = 0, d = 255;
    
            //统计现在灰度分布
            for(int i=0;i<rows;i++)
                for(int j = 0; j<cols;j++)
                {
                    double temp = image_1.at<uchar>(i,j);
                 //   cout<
                    if(temp > max)
                        max = temp;
                    if(temp < min)
                        min = temp;
                }
            cout<<min<<"     "<<max<<endl;
            //进行线性灰度变换
            for ( int i = 0; i<rows; i++)
                for ( int j = 0; j<cols; j++)
                {
                    double temp= ( d - c )/( max - min ) * (double(image_1.at<uchar>(i,j))- min)+c; 
                    image_1.at<uchar>(i,j) = (temp);
                }
                return image_1;
        }
        //如果是三通道的图片,需要对每个通道分别处理
        else
        {
            //cout<<"hhhhh"<
            vector<Mat>channels;
            split(image_1,channels);
            int count = 0;//记录当前通道数
    
            while(count!=channel)
            {         
                double c = 0, d = 255;
                double max = 0 , min = 255;
    
                for(int i = 0; i<rows;i++)
                    for (int j = 0; j<cols; j++)
                    {
                        double temp =channels[count].at<uchar>(i,j);
    //                    cout<
                        if(temp > max)
                            max = temp;
                        if(temp < min)
                            min = temp;
                   }
    
                for(int i = 0; i<rows;i++)
                    for (int j = 0; j<cols; j++)
                    {
                        double temp= ( d - c )/( max - min ) * (double(channels[count].at<uchar>(i,j))- min)+c; 
                        channels[count].at<uchar>(i,j) = temp;
                    }
                count++;
            }
    
            merge(channels, image_1);
            return image_1;
        }
    }
    
    
数字图像处理——扫描件背景处理_第3张图片

最 后

经过两次尝试后在阈值分割方法得到了几乎完美的结果,实现源码如下:

#include 
#include 
#include 

using namespace std;
using namespace cv;

// 图片路径
const string path = "/home/xz/coding/imageprocess/finalproject/test.jpg";

/**
 * @param image 待处理图像
 * 
 * 本函数用于处理图像扫描后背景有干扰内容
 */
Mat interatethreshold(Mat img);

/**
 * @param image 待变换图像
 * 
 * 本函数用以进行灰度线性变换,变换至0~255之间
 */
Mat linearTransform(Mat image);  

int main()
{
    Mat img = imread(path);
    Mat thres = interatethreshold(img);  //寻找阈值
    Mat result = linearTransform(thres); // 增强处理后的图片

    cout << "successfully processed" << endl;
    imshow("original", img);
    imshow("test_cut", thres);
    imshow("result", result);
    waitKey(0);
    destroyAllWindows();
    imwrite("/home/xz/coding/imageprocess/finalproject/nice.jpg", result);
    imwrite("/home/xz/coding/imageprocess/finalproject/_1.jpg", thres);
    return 0;
}

Mat interatethreshold(Mat image)
{
    Mat img = image.clone();
    vector<Mat>channels;
    split(img, channels);
    cout << "begin to find the threshold" << endl;
    for(int I = 0; I < 3; ++I)
    {
        double T1 = 0;
        double n1 = 0, count1 = 0, n2 = 0, count2 = 0;
        double T2 = 255; // 防止分母为0

        while(true)
        {
            for(int i = 0; i < (channels[I]).rows; ++i)
            {
                for(int j = 0; j < (channels[I]).cols; ++j)
                {
                    if(double((channels[I]).at<uchar>(i, j)) >= T1)
                    {
                        ++n1;
                       count1 += double((channels[I]).at<uchar>(i, j));  // 统计阈值两边的像素值和个数
                    }
                    else
                    {
                        ++n2;
                        count2 += double((channels[I]).at<uchar>(i, j));
                    }
                }
            }

            double temp1 = count1 / n1;
            double temp2 = count2 / n2;
            T2 = (temp1 + temp2) / 2.0;

            if(abs(T2 - T1) <= 1e-3)    
                break;

            else
            {
                T1 = T2;
            }

        }
        for(int i = 0; i < (channels[I]).rows; ++i)
        {
            for(int j = 0; j < (channels[I]).cols; ++j)
            {
                if((double)(channels[I]).at<uchar>(i, j) > T2)
                    (channels[I]).at<uchar>(i, j) = 255;
            }
        }
    }

    merge(channels, img);
    return img;
}

Mat linearTransform(Mat image)
{
    Mat image_1 = image.clone();
    //首先遍历图片,找出现在灰度值范围
    //首先检测是否是多通道图片
    //多通道图片需要分通道变换
    double channel = image_1.channels();
    int rows = image.rows,  cols = image.cols;

    //如果是单通道图片
    if(image.channels()==1)
    {
        double max = 0;
        double min = 255;
        double c = 0, d = 255;

        //统计现在灰度分布
        for(int i=0;i<rows;i++)
            for(int j = 0; j<cols;j++)
            {
                double temp = image_1.at<uchar>(i,j);
             //   cout<
                if(temp > max)
                    max = temp;
                if(temp < min)
                    min = temp;
            }
        cout<<min<<"     "<<max<<endl;
        //进行线性灰度变换
        for ( int i = 0; i<rows; i++)
            for ( int j = 0; j<cols; j++)
            {
                double temp= ( d - c )/( max - min ) * (double(image_1.at<uchar>(i,j))- min)+c; 
                image_1.at<uchar>(i,j) = (temp);
            }
            return image_1;
    }
    //如果是三通道的图片,需要对每个通道分别处理
    else
    {
        //cout<<"hhhhh"<
        vector<Mat>channels;
        split(image_1,channels);
        int count = 0;//记录当前通道数

        while(count!=channel)
        {         
            double c = 0, d = 255;
            double max = 0 , min = 255;

            for(int i = 0; i<rows;i++)
                for (int j = 0; j<cols; j++)
                {
                    double temp =channels[count].at<uchar>(i,j);
//                    cout<
                    if(temp > max)
                        max = temp;
                    if(temp < min)
                        min = temp;
               }

            for(int i = 0; i<rows;i++)
                for (int j = 0; j<cols; j++)
                {
                    double temp= ( d - c )/( max - min ) * (double(channels[count].at<uchar>(i,j))- min)+c; 
                    channels[count].at<uchar>(i,j) = temp;
                }
            count++;
        }

        merge(channels, image_1);
        return image_1;
    }
}

你可能感兴趣的:(C++程序设计)