图像实验2 - 直方图均衡

一、实验目的

掌握对图像直方图进行操作,实现图像的直方图均衡算法。
1、掌握求灰度图像归一化直方图方法
2、掌握灰度图像的直方图均衡算法
3、掌握对彩色图像进行直方图均衡的算法

二、实验内容

1、计算灰度图像的归一化直方图。
具体内容:利用 OpenCV 对图像像素进行操作,计算归一化直方图.并在
窗口中以图形的方式显示出来
2、灰度图像直方图均衡处理
具体内容:通过计算归一化直方图,设计算法实现直方图均衡化处理。
3、彩色图像直方图均衡处理
具体内容: 在灰度图像直方图均衡处理的基础上实现彩色直方图均衡处理。

三、实验完成情况

1、计算灰度图像的归一化直方图

核心代码

//计算归一化直方图
static Mat getHistogram(Mat img, double& min_value, double& max_value) {
    //图像数量
    int nimage = 1;
    //通道标号序列,这里由于只需要灰度,因此队列只有一通道
    int channels[] = { 0 };
    //可选,如果不为空,即8-bit且与图像同尺寸的矩阵序列,此时第i个图像被mask[i]覆盖的区域才会参与统计,此处取默认空值
    Mat mask;
    //输出直方图矩阵
    Mat hist;
    //直方图的维度/通道数,如取4则代表(a,b,c,d)为在第一个通道上为a,第二个通道上为b,第三个通道上为c,第四个通道上为d的像素统计数
    //此处只有灰度,因此只取1即可
    int dims = 1;
    //histSize[i]代表第i个维度/通道上直方图的直方条数量,这里采用256每个阶数占用一个直方条
    const int histSize[] = { 256 };
    //与uniform标记有关,如果uniform为true,则range[i]两个值代表直方图的第i个维度上每个直方条的上下限,如果为false则range[i]的histSize[i]+1个值分别代表每个直方条的上下限
    float hrange[] = { 0,255 };
    const float* range[] = { hrange };
    //代表直方条是否均匀等宽,true为等宽,false为不等
    bool uniform = true;
    //标记直方图重新分配时是否清零,如果为否则可以进行累加
    bool accumulate = false;
    //计算直方图
    calcHist(&img, nimage, channels, mask, hist, dims, histSize, range, uniform, accumulate);

    //计算灰度最小和最大值
    minMaxLoc(hist, &min_value, &max_value);

    //直方图画布,采用8bit位深,无符号整型以及3通道图片进行绘制
    Mat target = Mat::zeros(Size(256, 300), CV_8UC3);
    //绘制直方图
    for (int top, i = 0; i < 256; i++) {
        //计算直方条上界
        top = cvRound(hist.at<float>(i)/max_value*256);
        //绘制直方条
        line(target, Point(i, target.rows - 1), Point(i, target.rows - 1 - top), Scalar::all(255));
    }
    return target;
}

实现截图


2、灰度图像直方图均衡处理

核心代码

//直方图均衡化处理
void channelEqualization(Mat &layer) {
    //均衡化
    equalizeHist(layer, layer);
}

//均衡化手动函数
void myChannelEqualization(Mat & layer) {
    //总像素数
    double sum = (double)img.rows * (double)img.cols;
    //各灰度总和
    double cnt[256] = { 0 };
    //均衡化像素数
    uchar equal[256];
    //统计各灰度值总数
    for (int i = 0; i < layer.rows; i++) {
        uchar* ptr = layer.ptr(i);
        for (int j = 0; j < layer.cols; j++) {
            cnt[ptr[j]]++;
        }
    }
    //获取均衡化像素密度
    cnt[0] /= sum;
    for (int i = 1; i < 256; i++)
        cnt[i] = cnt[i] / sum + cnt[i - 1];
    //获取均衡化后灰度值
    for (int i = 0; i < 256; i++)
        equal[i] = (uchar)(255 * cnt[i]);

    //替换均衡化灰度
    for (int i = 0; i < layer.rows; i++) {
        uchar* ptr = layer.ptr(i);
        for (int j = 0; j < layer.cols; j++) {
            ptr[j] = equal[ptr[j]];
        }
    }
}

//均衡化预处理
Mat handleEqualization(bool hsv, bool my) {
    Mat target = img.clone();
    //彩色图像处理
    if (color) {
        Mat result;
        vector<Mat> channels;
        //HSV方式处理
        if (hsv) {
            //转换为HSV图像
            cvtColor(target, target, COLOR_BGR2HSV);
            //分离通道
            split(target, channels);
            //处理亮度层,不影响色彩
            my ? myChannelEqualization(channels[2]) : channelEqualization(channels[2]);
            //将亮度层组合
            merge(channels, result);
            //转换回RGB图像
            cvtColor(result, result, COLOR_HSV2BGR);
        }
        //RGB方式处理
        else {
            //分离RGB通道
            split(target, channels);
            //每个通道单独均衡化处理
            for (int i = 0; i < channels.size(); i++)
                my ? myChannelEqualization(channels[i]) : channelEqualization(channels[i]);
            //将图层组合
            merge(channels, result);
        }
        return result;
    }
    //灰阶图像处理
    else {
        my ? myChannelEqualization(target) : channelEqualization(target); 
        return target;
    }
}

实现截图



3、彩色图像直方图均衡处理

核心代码

同灰度均衡处理代码,参数color为true即可。

实现截图


RGB均衡处理
RGB均衡直方图
HSV均衡处理
HSV均衡直方图

四、实验中的问题

  1. OpenCV中关于计算直方图的函数calcHist参数较多切较难理解。
  2. 灰度和彩色均衡化处理的过程有所区别,主要为通道数的不同,起初统一处理时出现均衡化失败或者只有单通道均衡化的问题。

五、实验结果

源码

lab2.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>

using namespace std;
using namespace cv;

const static std::string path = "F:\\Documents\\高级图像处理\\Image\\";

//图片路径组合
string getFullPath(string name) {
    return path + name;
}

Mat show_h(Mat img) {
    int channels = 0;
    MatND dstHist;
    //设定像素取值范围
    int hisSize[] = { 256 };
    float midRanges[] = { 0,255 };
    const float* ranges[] = { midRanges };
    //计算直方图
    calcHist(&img, 1, &channels, Mat(), dstHist, 1, hisSize, ranges, true, false);
    Mat drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
    double MaxValue;
    minMaxLoc(dstHist, 0, &MaxValue, 0, 0);//图像最小最大值
    for (int i = 0; i < 256; i++) {
        int value = cvRound((float)dstHist.at<float>(i) * 256 * 0.9 / MaxValue);//四舍五入
        //在直方图画布上画出直方图
        line(drawImage, Point(i, drawImage.rows - 1), Point(i, drawImage.rows - 1 - value), Scalar(255, 255, 255));
    }
    return drawImage;
}

//打开图片显示窗口
void openWindows(string win_name, Mat img, int x = 500, int y = 500) {
    //窗口命名,指定大小,生成位置
    namedWindow(win_name, WINDOW_AUTOSIZE);
    moveWindow(win_name, x, y);
    //生成窗口显示图片
    imshow(win_name, img);
    //等待键入
    waitKey();
    //关闭窗口
    destroyWindow(win_name);
}

//统一数字输入函数
template<typename T>T inputNumber(string desc) {
    system("cls");
    T input;
    cout << desc;
    cin >> input;
    cout << endl;
    return input;
}

//统一图片打开函数,用于简化路径和处理打开图片错误
Mat openImage(string name, int type = 1) {
    //图片读取函数,返回图像存储类(包含存储方式、存储矩阵、矩阵大小等)
    Mat img = imread(getFullPath(name), type);
    if (img.empty()) {
        cout << "无效图片,读取失败" << endl;
        exit(-1);
    }
    return img;
}

//图像基类
class Image {
protected:
    Mat img;
public:
    Mat getImage() {
        return img.clone();
    }
};

//彩色图像处理类
class ColorImage :public Image {
public:
    //读取彩色图像并展示
    ColorImage(string path) {
        img = openImage(path, IMREAD_COLOR);
        openWindows("彩色图像", img, false);
    }
};

//灰度图像处理类
class GrayImage :public Image {
public:
    //仅读取灰度方式读取图像并展示(IMREAD_GRAYSCALE)
    GrayImage(string path) {
        img = openImage(path, IMREAD_GRAYSCALE);
        openWindows("灰度图像", img, false);
    }
};

class Histogram {
private:
    bool color;
    Mat img;
    double min_value;
    double max_value;

    //计算归一化直方图
    static Mat getHistogram(Mat img, double& min_value, double& max_value) {
        //图像数量
        int nimage = 1;
        //通道标号序列,这里由于只需要灰度,因此队列只有一通道
        int channels[] = { 0 };
        //可选,如果不为空,即8-bit且与图像同尺寸的矩阵序列,此时第i个图像被mask[i]覆盖的区域才会参与统计,此处取默认空值
        Mat mask;
        //输出直方图矩阵
        Mat hist;
        //直方图的维度/通道数,如取4则代表(a,b,c,d)为在第一个通道上为a,第二个通道上为b,第三个通道上为c,第四个通道上为d的像素统计数
        //此处只有灰度,因此只取1即可
        int dims = 1;
        //histSize[i]代表第i个维度/通道上直方图的直方条数量,这里采用256每个阶数占用一个直方条
        const int histSize[] = { 256 };
        //与uniform标记有关,如果uniform为true,则range[i]两个值代表直方图的第i个维度上每个直方条的上下限,如果为false则range[i]的histSize[i]+1个值分别代表每个直方条的上下限
        float hrange[] = { 0,255 };
        const float* range[] = { hrange };
        //代表直方条是否均匀等宽,true为等宽,false为不等
        bool uniform = true;
        //标记直方图重新分配时是否清零,如果为否则可以进行累加
        bool accumulate = false;
        //计算直方图
        calcHist(&img, nimage, channels, mask, hist, dims, histSize, range, uniform, accumulate);

        //计算灰度最小和最大值
        minMaxLoc(hist, &min_value, &max_value);

        //直方图画布,采用8bit位深,无符号整型以及3通道图片进行绘制
        Mat target = Mat::zeros(Size(256, 300), CV_8UC3);
        //绘制直方图
        for (int top, i = 0; i < 256; i++) {
            //计算直方条上界
            top = cvRound(hist.at<float>(i)/max_value*256);
            //绘制直方条
            line(target, Point(i, target.rows - 1), Point(i, target.rows - 1 - top), Scalar::all(255));
        }
        return target;
    }

    //直方图均衡化处理
    void channelEqualization(Mat &layer) {
        //均衡化
        equalizeHist(layer, layer);
    }

    //均衡化手动函数
    void myChannelEqualization(Mat & layer) {
        //总像素数
        double sum = (double)img.rows * (double)img.cols;
        //各灰度总和
        double cnt[256] = { 0 };
        //均衡化像素数
        uchar equal[256];
        //统计各灰度值总数
        for (int i = 0; i < layer.rows; i++) {
            uchar* ptr = layer.ptr(i);
            for (int j = 0; j < layer.cols; j++) {
                cnt[ptr[j]]++;
            }
        }
        //获取均衡化像素密度
        cnt[0] /= sum;
        for (int i = 1; i < 256; i++)
            cnt[i] = cnt[i] / sum + cnt[i - 1];
        //获取均衡化后灰度值
        for (int i = 0; i < 256; i++)
            equal[i] = (uchar)(255 * cnt[i]);

        //替换均衡化灰度
        for (int i = 0; i < layer.rows; i++) {
            uchar* ptr = layer.ptr(i);
            for (int j = 0; j < layer.cols; j++) {
                ptr[j] = equal[ptr[j]];
            }
        }
    }

    //均衡化预处理
    Mat handleEqualization(bool hsv, bool my) {
        Mat target = img.clone();
        //彩色图像处理
        if (color) {
            Mat result;
            vector<Mat> channels;
            //HSV方式处理
            if (hsv) {
                //转换为HSV图像
                cvtColor(target, target, COLOR_BGR2HSV);
                //分离通道
                split(target, channels);
                //处理亮度层,不影响色彩
                my ? myChannelEqualization(channels[2]) : channelEqualization(channels[2]);
                //将亮度层组合
                merge(channels, result);
                //转换回RGB图像
                cvtColor(result, result, COLOR_HSV2BGR);
            }
            //RGB方式处理
            else {
                //分离RGB通道
                split(target, channels);
                //每个通道单独均衡化处理
                for (int i = 0; i < channels.size(); i++)
                    my ? myChannelEqualization(channels[i]) : channelEqualization(channels[i]);
                //将图层组合
                merge(channels, result);
            }
            return result;
        }
        //灰阶图像处理
        else {
            my ? myChannelEqualization(target) : channelEqualization(target); 
            return target;
        }
    }

public:
    Histogram(Image img) {
        this->img = img.getImage();
        this->color = this->img.channels() != 1;
    }

    //归一化直方图
    void show() {
        openWindows("直方图", Histogram::getHistogram(img, min_value, max_value));
    }

    //直方图均衡化处理
    void equalization(bool hsv = false,bool my = false) {
        Mat equal_img = handleEqualization(hsv,my);
        openWindows("原图", img);
        openWindows("均衡化图像", equal_img);
        openWindows("均衡化直方图", Histogram::getHistogram(equal_img, min_value, max_value));
    }
};

int main() {

    string name = "test.jpg";
    
    //灰度图像 
    GrayImage gray_img(name);

    //灰阶直方图
    Histogram ghist(gray_img);

    //归一化直方图
    ghist.show();

    //均衡化
    ghist.equalization();

    //彩色图像
    ColorImage color_img(name);

    //彩色直方图
    Histogram chist(color_img);

    //均衡化
    chist.equalization(false);

    return 0;
}

posted @ 2021-06-15 21:22  wsxuehao  阅读(923)  评论(0)    收藏  举报