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

数字图像处理实验


实验目的

去除如图所示图片背景(图片来源见水印):

original

第一次尝试

在网上查询资料后发现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;

}

_1.jpg

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

  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<<temp<<endl;

                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"<<endl;

        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<<temp<<endl;

                    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;

    }

}



nice.jpg

最 后

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


#include <opencv2/opencv.hpp>

#include <iostream>

#include <cmath>

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<<temp<<endl;

                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"<<endl;

        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<<temp<<endl;

                    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;

    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 227,837评论 6 531
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,196评论 3 414
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 175,688评论 0 373
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 62,654评论 1 309
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,456评论 6 406
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 54,955评论 1 321
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,044评论 3 440
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,195评论 0 287
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 48,725评论 1 333
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,608评论 3 354
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 42,802评论 1 369
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,318评论 5 358
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,048评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,422评论 0 26
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,673评论 1 281
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,424评论 3 390
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 47,762评论 2 372