zoukankan      html  css  js  c++  java
  • 直方图均衡化的 C++ 实现(基于 openCV)

    这是数字图像处理课的大作业,完成于 2013/06/17,需要调用 openCV 库,完整源码和报告如下:

      1 #include <cv.h>
      2 #include <highgui.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <math.h>
      6 #include <assert.h>
      7 #include <string>
      8 
      9 /* 灰度级结点 */
     10 typedef struct {
     11     int pixels;        // 灰度级对应像素个数
     12     float rate;        // 像素比例
     13     float accuRate;    // 累计像素比例
     14     int map;        // 到均衡化后的灰度级的映射
     15 } levNode;
     16 
     17 void histeqGray(IplImage* pGray, int levels, int argc);
     18 IplImage* histImage(IplImage* pSrc, int histWidth, int histHeight, int nScale);
     19 
     20 int main(int argc, char* argv[])
     21 {
     22     int levels;
     23     std::string imgName, inTmp;
     24     if (argc == 3) {
     25         levels = atoi(argv[1]);
     26         imgName = argv[2];
     27     } 
     28     else if (argc == 2)
     29         imgName = argv[1];
     30     else {
     31         printf("usage: histeq [levels] image_name 
    ");
     32         return -1;
     33     }
     34     
     35     IplImage* pSrc = cvLoadImage(imgName.c_str(), CV_LOAD_IMAGE_UNCHANGED);
     36     int channel = pSrc->nChannels;
     37 
     38     IplImage* pChnl[4] = { NULL };
     39 
     40     for (int i = 0; i < channel; ++i)
     41         pChnl[i] = cvCreateImage(cvGetSize(pSrc), pSrc->depth, 1);
     42 
     43     cvSplit(pSrc, pChnl[0], pChnl[1], pChnl[2], pChnl[3]);
     44     
     45     for (int i = 0; i < channel; ++i)
     46         histeqGray(pChnl[i], levels, argc);
     47 
     48     IplImage* pEql = cvCreateImage(cvGetSize(pSrc), pChnl[0]->depth, pSrc->nChannels);
     49 
     50     cvMerge(pChnl[0], pChnl[1], pChnl[2], pChnl[3], pEql);
     51 
     52     inTmp = imgName + "_Eql.jpg";
     53     cvSaveImage(inTmp.c_str(), pEql);
     54 
     55     //cvNamedWindow(imgName.c_str(), CV_WINDOW_AUTOSIZE);
     56     cvShowImage(imgName.c_str(), pSrc);
     57     //cvNamedWindow(inTmp.c_str(), CV_WINDOW_AUTOSIZE);
     58     cvShowImage(inTmp.c_str(), pEql);
     59 
     60     IplImage* pSrcGray = cvCreateImage(cvGetSize(pSrc), IPL_DEPTH_8U, 1);
     61     if (pSrc->nChannels == 3)
     62         cvCvtColor(pSrc, pSrcGray, CV_BGR2GRAY);
     63     else
     64         cvCopyImage(pSrc, pSrcGray);
     65     IplImage* pEqlGray = cvCreateImage(cvGetSize(pEql), IPL_DEPTH_8U, 1);
     66     if (pSrc->nChannels == 3)
     67         cvCvtColor(pEql, pEqlGray, CV_BGR2GRAY);
     68     else
     69         cvCopyImage(pEql, pEqlGray);
     70     imgName += "_Hist.jpg";
     71     inTmp += "_Hist.jpg";
     72     int nScale = 2;
     73     int histWidth = /*pSrc->width * nScale*/256 * nScale;
     74     int histHeight = /*pSrc->height*/128;
     75     IplImage* pSrcGrayHist = histImage(pSrcGray, histWidth, histHeight, nScale);
     76     IplImage* pEqlGrayHist = histImage(pEqlGray, histWidth, histHeight, nScale);
     77     cvSaveImage(imgName.c_str(), pSrcGrayHist);
     78     cvSaveImage(inTmp.c_str(), pEqlGrayHist);
     79     cvShowImage(imgName.c_str(), pSrcGrayHist);
     80     cvShowImage(inTmp.c_str(), pEqlGrayHist);
     81 
     82     cvWaitKey();
     83 
     84     cvReleaseImage(&pEql);
     85     cvReleaseImage(&pEqlGray);
     86     for (int i = 0; i < channel; ++i)
     87         cvReleaseImage(&pChnl[i]);
     88     cvReleaseImage(&pSrc);
     89     cvReleaseImage(&pSrcGray);
     90 
     91     return 0;
     92 }
     93 
     94 /*
     95 * 直方图均衡化函数 
     96 * pGray为输入的灰度图
     97 * levels为均衡化的灰度级
     98 */
     99 void histeqGray(IplImage* pGray, int levels, int argc)
    100 {
    101     int depth = pGray->depth;
    102     printf("%d 
    ", depth);
    103     int width = pGray->width;
    104     int height = pGray->height;
    105     int sumPixels = width * height;        // 总像素数
    106     printf("%d 
    ", sumPixels);
    107     int values = static_cast<int>(pow((float)2, depth)); // 根据图像深度计算像素取值范围
    108     if (argc == 2) levels = values;
    109     printf("%d 
    ", levels);
    110 
    111     int outDepth;
    112     /*if (levels <= 2)
    113         outDepth = 1;
    114     else*/ if (levels <= 256)
    115         outDepth = 8;
    116     else if (levels <= 65536)
    117         outDepth = 16;
    118 
    119     assert(levels <= values);
    120     int intervals = values / levels; // 根据像素取值范围和灰度级求每个灰度级的像素间隔
    121     levNode* levNodes = (levNode*)calloc(levels, sizeof(levNode)); // 生成灰度结点
    122     //for (int lev = 0; lev < levels; ++lev) printf("%d 
    ", levNodes[lev].pixels);
    123     //char* pValues = pGray->imageData;
    124 
    125     /* 统计每个灰度级的像素个数 */
    126     for (int y = 0; y < height; ++y)
    127         for (int x = 0; x < width; ++x) {
    128             CvScalar scal = cvGet2D(pGray, y, x);
    129             int val = (int)scal.val[0];
    130             //printf("%d 
    ", val);
    131             for (int lev = 0; lev < levels; ++lev) {
    132                 if ( val >= intervals*lev && val < intervals*(lev+1)) {
    133                     ++levNodes[lev].pixels; break;
    134                 }
    135             }
    136         }
    137 
    138     int sum = 0;
    139     for (int lev = 0; lev < levels; ++lev)
    140         sum += levNodes[lev].pixels;
    141     printf("%d 
    ", sum);
    142 
    143     /* 计算每个灰度级像素比例和累计比例 */
    144     levNodes[0].accuRate = levNodes[0].rate = levNodes[0].pixels / (float)sumPixels;
    145     levNodes[0].map = (int)(levNodes[0].accuRate * (levels - 1) + 0.5);
    146     printf("%d 
    ", levNodes[0].pixels);
    147     for (int lev = 1; lev < levels; ++lev) {
    148         levNodes[lev].rate = levNodes[lev].pixels / (float)sumPixels;
    149         levNodes[lev].accuRate = levNodes[lev-1].accuRate + levNodes[lev].rate;
    150         levNodes[lev].map = (int)(levNodes[lev].accuRate * (levels - 1) + 0.5);
    151     }
    152     printf("%f 
    ", levNodes[levels-1].accuRate);
    153 
    154     /* 生成均衡化后的图像 */
    155     for (int y = 0; y < height; ++y)
    156         for (int x = 0; x < width; ++x) {
    157             CvScalar scal = cvGet2D(pGray, y, x);
    158             int val = (int)scal.val[0];
    159             //printf("%d 
    ", val);
    160             for (int lev = 0; lev < levels; ++lev) {
    161                 if (val >= intervals*lev && val < intervals*(lev+1)) {                    
    162                         scal.val[0] = levNodes[lev].map;
    163                         //printf("%f 
    ", scal.val[0]);
    164                         cvSet2D(pGray, y, x, scal);
    165                         break;
    166                 }
    167             }
    168         }
    169     pGray->depth = outDepth;
    170 
    171     free(levNodes);
    172 }
    173 
    174 /*
    175 * 绘制直方图函数
    176 */
    177 IplImage* histImage(IplImage* pSrc, int histWidth, int histHeight, int nScale)
    178 {
    179     int histSize = static_cast<int>(pow((float)2, pSrc->depth));
    180     CvHistogram* pHist = cvCreateHist(/*pSrc->nChannels*/1, &histSize, CV_HIST_ARRAY);
    181     cvCalcHist(&pSrc, pHist);
    182 
    183     IplImage* pHistImg = cvCreateImage(cvSize(histWidth, histHeight), IPL_DEPTH_8U, 1);
    184     cvRectangle(pHistImg, cvPoint(0,0), cvPoint(pHistImg->width,pHistImg->height), CV_RGB(255,255,255), CV_FILLED);
    185 
    186     float histMaxVal = 0;
    187     cvGetMinMaxHistValue(pHist, 0, &histMaxVal);
    188 
    189     for(int i = 0; i < histSize; i++)
    190     {
    191         float histValue= cvQueryHistValue_1D(pHist, i); // 像素为i的直方块大小
    192         int nRealHeight = cvRound((histValue / histMaxVal) * histHeight);  // 要绘制的高度
    193         cvRectangle(pHistImg,  
    194             cvPoint(i*nScale, histHeight - 1),  
    195             cvPoint((i + 1)*nScale - 1, histHeight - nRealHeight),
    196             cvScalar(i),   
    197             CV_FILLED
    198         );   
    199     }
    200     //cvFillConvexPoly
    201 
    202     cvReleaseHist(&pHist);
    203     return pHistImg;
    204 }
    View Code

    一、直方图均衡化概述

    直方图均衡化是一种图像增强方法,其基本思想是把给定图像的直方图分布改造成均匀分布的直方图,从而增加象素灰度值的动态范围,达到增强图像整体对比度的效果。由信息学的理论来解释,具有最大熵(信息量)的图像为均衡化图像。

    直方图均衡化可表示为:clip_image002,t为某个象素变换后的灰度级,s为该象素变换前的灰度级。

    该灰度变换函数应满足如下两个条件:

    1)f(s)在clip_image004范围内是单值递增函数;

    2)对clip_image006

    条件1:保证原图各灰度级在变换后仍保持从黑到白(或从白到黑)的排列顺序;

    条件2:保证变换前后灰度值动态范围的一致性。

    可以证明累积分布函数(cumulative distribution function CDF)满足上述两个条件并能将s的分布转换为t的均匀分布。

    事实上,s的CDF就是原始图的累积直方图,即:

    clip_image008

    其中clip_image010clip_image012、k=0,1,…,L-1

    根据这个公式,可以直接算出直方图均衡化后各象素的灰度值。clip_image014需要取整,以满足数字图象的要求。

     

    二、算法步骤

    步骤

    运算

    1

    列出原始图灰度级 clip_image016

    2

    统计原始直方图各灰度级象素数 clip_image018

    3

    计算原始直方图(像素比例)

    4

    计算累积直方图 clip_image020(累计像素比例)

    5

    取整clip_image022

    6

    确定映射对应关系( clip_image016[1]clip_image020[1]

    7

    计算新的直方图

    三、算法测试

    1、灰度图

    clip_image024clip_image026

    clip_image028

    clip_image030

    2、彩色图

    clip_image032clip_image034

    clip_image036

    clip_image038

     

    四、结果分析

    (1)对于灰度图和彩色图,算法结果都不错,直方图显示像素分布很广、很平均。

    (2)直方图均衡化的优点:自动增强整个图像的对比度。

    (3)直方图均衡化的不足:具体增强效果不易控制,处理的结果总是得到全局均衡化的直方图。

  • 相关阅读:
    CSS3中三种清除浮动(float)影响的方式
    HTML中关于动态创建的标签无法绑定js事件的解决方法:.on()方法的 [.selector]
    Android 5.0以上heads up通知
    CoordinatorLayout
    ViewDragHelper
    Transition FrameWork
    Android启动过程
    不要滥用SharedPreference
    不要在Application中缓存数据
    SparseArray替代HashMap来提高性能
  • 原文地址:https://www.cnblogs.com/keke2014/p/3859124.html
Copyright © 2011-2022 走看看