OpenCV实现基本灰度变换

OpenCV小白一枚,最近在看数字图像处理第二版,看课本的同时想加深一下理解,自己动手写一些代码实现一下课本中的效果。

看到第三章了,实现一些基本灰度变换的效果。不同灰度变换只是使用的公式不同而已,核心思想都是在空间域直接对每一个像素的灰度值进行判断处理,代码很简单。

//基本灰度变换

#include<iostream>
#include<cmath>
#include<fstream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>


using namespace std;
using namespace cv;


#define CUTTING_MAINTAIN 1
#define CUTTING_LOW 0


//查看像素值
void View(Mat src)
{
    ofstream PView;
    PView.open("PView.txt");
    int num = 0;
    for (int i = 0;i < src.rows;i++)
    {
        for (int j = 0;j < src.cols;j++)
        {
            if (num == 50)
            {
                num = 0;
                PView << endl;
            }
            else
            {
                int a = src.at<uchar>(i, j);
                PView << a << " ";
                num++;
            }
        }
    }
    PView.close();
}


//图像反转
void Reverse(Mat src,Mat intermediate)
{
    for (int i = 0;i < src.rows;i++)
    {
        for (int j = 0;j < src.cols;j++)
        {
            intermediate.at<uchar>(i, j) = 255 - src.at<uchar>(i, j);
        }
    }
}


//对数变换
void Log(Mat src, Mat intermediate,int c)
{
    for (int i = 0;i < src.rows;i++)
    {
        for (int j = 0;j < src.cols;j++)
        {
            int b = c*log(1+src.at<uchar>(i, j));
            int d = b % 256;
            intermediate.at<uchar>(i, j) = d;
        }
    }
}


//幂次变换
void Power(Mat src, Mat intermediate,float gamma)
{
    for (int i = 0;i < src.rows;i++)
    {
        for (int j = 0;j < src.cols;j++)
        {
            float c = pow(255, 1 - gamma);
            int b = c*pow(src.at<uchar>(i, j), gamma);
            int d = b % 256;
            intermediate.at<uchar>(i, j) = d;
        }
    }
}


//对比拉伸
void Stretch(Mat src, Mat intermediate,float r1, float s1, float r2, float s2 )
{
    float k1, k2, k3, b1, b2, b3;
    k1 = s1 / r1;
    b1 = 0.0;
    k2 = (s2 - s1) / (r2 - r1);
    b2 = (s1*r2 - s2*r1) / (r2 - r1);
    k3 = (255.0 - s2) / (255.0 - r2);
    b3 = (255.0*s2-r2*255.0) / (255.0 - r2);
    cout  << b3;
    for (int i = 0;i < src.rows;i++)
    {
        for (int j = 0;j < src.cols;j++)
        {
            if (src.at<uchar>(i, j) >= 0.0 && src.at<uchar>(i, j) <= r1)
            {
                int a = k1*src.at<uchar>(i, j) + b1;
                int b = a % 256;
                intermediate.at<uchar>(i, j) = b;
            }
            if (src.at<uchar>(i, j) >= r1 && src.at<uchar>(i, j) <= r2)
            {
                int a = k2*src.at<uchar>(i, j) + b2;
                int b = a % 256;
                intermediate.at<uchar>(i, j) = b;
            }
            if (src.at<uchar>(i, j) >= r2 && src.at<uchar>(i, j) <= 255.0)
            {
                int a = k3*src.at<uchar>(i, j) + b3;
                int b = a % 256;
                intermediate.at<uchar>(i, j) = b;
            }
        }
    }
}


//灰度切割
void Cutting(Mat src, Mat intermediate, float r1,float r2,int type,int low,int high)
{
    if (type == CUTTING_MAINTAIN)
    {
        for (int i = 0;i < src.rows;i++)
        {
            for (int j = 0;j < src.cols;j++)
            {
                if (src.at<uchar>(i, j) >= 0.0 && src.at<uchar>(i, j) <= r1)
                {
                    intermediate.at<uchar>(i, j) = src.at<uchar>(i, j);
                }
                if (src.at<uchar>(i, j) >= r1 && src.at<uchar>(i, j) <= r2)
                {
                    intermediate.at<uchar>(i, j) = high;
                }
                if (src.at<uchar>(i, j) >= r2 && src.at<uchar>(i, j) <= 255.0)
                {
                    intermediate.at<uchar>(i, j) = src.at<uchar>(i, j);
                }
            }
        }
    }
    else
    {
        for (int i = 0;i < src.rows;i++)
        {
            for (int j = 0;j < src.cols;j++)
            {
                if (src.at<uchar>(i, j) >= 0.0 && src.at<uchar>(i, j) <= r1)
                {
                    intermediate.at<uchar>(i, j) = low;
                }
                if (src.at<uchar>(i, j) >= r1 && src.at<uchar>(i, j) <= r2)
                {
                    intermediate.at<uchar>(i, j) = high;
                }
                if (src.at<uchar>(i, j) >= r2 && src.at<uchar>(i, j) <= 255.0)
                {
                    intermediate.at<uchar>(i, j) = low;
                }
            }
        }
    }
}


int main()
{
    Mat src = cv::imread("Spectrum.tif", 0),temp;
    Mat intermediate(src.rows, src.cols, CV_8UC1);
    //cv::resize(src, temp, Size(src.cols*0.6, src.rows*0.6));
    cv::imshow("Source", src);

    //View(src);
    Reverse(src, intermediate);
    //Log(src, intermediate,45);
    //Power(src, intermediate, 0.8);
    //Stretch(src, intermediate,32.0,96.0,160.0,224.0);
    //Cutting(src, intermediate, 50.0, 100.0,CUTTING_MAINTAIN,0,255);

    //cv::resize(intermediate, intermediate, Size(intermediate.cols*0.6, intermediate.rows*0.6));
    cv::imshow("intermediate", intermediate);

    cv::imwrite("D:\\Computer Software\\Visual Studio 2015\\Projects\\ImageProcessing\\ImageProcessing\\检测结果Spectrum-Reverse.tif", intermediate);

    cv::waitKey(0);
    return 0;
}

代码里处理的图片都是《数字图像处理》的源图像,第二版的好像不再出版了,只找到了第三版的源图像,不过差不太多的。

书上的源图像和课后问题图像可以到这个网站下载:
数字图像处理各版本源图像及课后问题图像

附部分检测结果的图片:
1.幂次变换用于增强对比度:
源图像:幂次变换(γ=0.4):
2.灰度切割:
源图像:

增强[50,100]区间灰度值为255,其余灰度值保持不变:

增强[50,100]区间灰度值为255,其余灰度值变化为0:

改进一下:
实现鼠标交互操作:使用鼠标圈出矩形框,只对矩形框内像素进行处理,可以更直观的观察某种算法对图像产生的变化。下面以对比拉伸为例:

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


using namespace std;
using namespace cv;

Rect rect;
bool rect_flag = false;

//获取鼠标所选取的矩形
void onMouse(int event,int x,int y,int flags,void*param)
{
    if (event == CV_EVENT_LBUTTONDOWN)
    {
        rect.x = x;
        rect.y = y;
        rect_flag = false;
    }
    if (event == CV_EVENT_LBUTTONUP)
    {
        rect.width = x - rect.x;
        rect.height = y - rect.y;
        rect_flag = true;
    }
}


//对比拉伸
void Stretch(Mat src, Mat intermediate,float r1, float s1, float r2, float s2 )
{
    float k1, k2, k3, b1, b2, b3;
    k1 = s1 / r1;
    b1 = 0.0;
    k2 = (s2 - s1) / (r2 - r1);
    b2 = (s1*r2 - s2*r1) / (r2 - r1);
    k3 = (255.0 - s2) / (255.0 - r2);
    b3 = (255.0*s2-r2*255.0) / (255.0 - r2);
    for (int i = rect.y;i < rect.y+rect.height;i++)
    {
        for (int j = rect.x;j < rect.x+rect.width;j++)
        {
            if (src.at<uchar>(i, j) >= 0.0 && src.at<uchar>(i, j) <= r1)
            {
                int a = k1*src.at<uchar>(i, j) + b1;
                int b = a % 256;
                intermediate.at<uchar>(i, j) = b;
            }
            if (src.at<uchar>(i, j) >= r1 && src.at<uchar>(i, j) <= r2)
            {
                int a = k2*src.at<uchar>(i, j) + b2;
                int b = a % 256;
                intermediate.at<uchar>(i, j) = b;
            }
            if (src.at<uchar>(i, j) >= r2 && src.at<uchar>(i, j) <= 255.0)
            {
                int a = k3*src.at<uchar>(i, j) + b3;
                int b = a % 256;
                intermediate.at<uchar>(i, j) = b;
            }
        }
    }
}

int main()
{
    Mat src = cv::imread("Pollen.tif", 0),temp, intermediate;
    cv::resize(src, temp, Size(src.cols*0.6, src.rows*0.6));
    cv::imshow("Source", temp);

    namedWindow("intermediate",1);
    setMouseCallback("intermediate", onMouse, 0);   

    while (1)
    {
        cv::resize(src, intermediate, Size(src.cols*0.6, src.rows*0.6));
        //消除第一次画矩形后第二次点击鼠标直接在点击位置画出与上一个矩形等大矩形的情况
        if (rect_flag)
        {
            Stretch(temp, intermediate, 100.0, 1.0, 120.0, 254.0);
            cv::rectangle(intermediate, rect, Scalar(255, 0, 0), 1);
        }
        cv::imshow("intermediate", intermediate);

        //ESC按键
        char c = (char)waitKey(20);
        if (27 == c)
            return -1;
    }

    return 0;
}

效果图

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值