(学习笔记5)BMP位图照片的灰度变换之二值化

来源:互联网 发布:linux net snmp使用 编辑:程序博客网 时间:2024/06/05 04:57

这次我主要在BMP位图照片的读取和显示的基础上,加上了对BMP位图照片的灰度变换。都是一层层上来的,这次不打算一次性更新完灰度变换,我会一个个来更新,这来更新的节奏会快一点,避免一次内容太多,撰写时间过长。

废话不多说,直接上干货。
灰度变换包括以下几个操作:
二值化
直方图
直方图均衡
反转
阀值变换
分段线性拉伸

step1. 绘制菜单栏
这里写图片描述

step2. 接下来,我们对”灰度变换“下的”二值化“进行修改ID。如下图所示:
这里写图片描述

step3.添加GrayTrans这个类。如下图:
这里写图片描述
GrayTrans.h中的代码如下:

// GrayTrans.h: interface for the GrayTrans class.////////////////////////////////////////////////////////////////////////#if !defined(AFX_GRAYTRANS_H__8F98F0EE_4F7D_4138_8573_4E4A065784C2__INCLUDED_)#define AFX_GRAYTRANS_H__8F98F0EE_4F7D_4138_8573_4E4A065784C2__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include "ImageDib.h"class GrayTrans : public ImageDib  {public:    //输出图像每像素位数    int m_nBitCountOut;    //输出图像位图数据指针    unsigned char * m_pImgDataOut;    //输出图像颜色表    LPRGBQUAD m_lpColorTableOut;    //输出图像的宽,像素为单位    int m_imgWidthOut;    //输出图像的高,像素为单位    int m_imgHeightOut;    //输出图像颜色表长度    int m_nColorTableLengthOut;public:    //不带参数的构造函数    GrayTrans();    //带参数的构造函数    GrayTrans(CSize size, int nBitCount, LPRGBQUAD lpColorTable,         unsigned char *pImgData);    //析构函数    ~GrayTrans();    //以像素为单位返回输出图像的宽和高    CSize GetDimensions();    //二值化    void BinaryImage(int threshold=128);    //反转    void RevImage();    //窗口变换    void ThresholdWindow(int bTop, int bBottom);    //线性拉伸    void LinearStrech(CPoint point1,CPoint point2);private:    //单通道数据线性拉伸    void LinearStrechForSnglChannel(unsigned char *pImgDataIn,        unsigned char *pImgDataOut,int imgWidth,int imgHeight,        CPoint point1,CPoint point2);};#endif // !defined(AFX_GRAYTRANS_H__8F98F0EE_4F7D_4138_8573_4E4A065784C2__INCLUDED_)

这里写图片描述

GrayTrans.cpp中的代码如下:

// GrayTrans.cpp: implementation of the GrayTrans class.////////////////////////////////////////////////////////////////////////#include "stdio.h"#include "stdafx.h"#include "demo1.h"#include "GrayTrans.h"#include "math.h"#ifdef _DEBUG#undef THIS_FILEstatic char THIS_FILE[]=__FILE__;#define new DEBUG_NEW#endif/************************************************************************ 函数名称: GrayTrans()* 说明:无参数的构造函数,对成员变量进行初始化***********************************************************************/GrayTrans::GrayTrans(){    m_pImgDataOut=NULL;//输出图像位图数据指针为空    m_lpColorTableOut=NULL;//输出图像颜色表指针为空    m_nColorTableLengthOut=0;//输出图像颜色表长度为0    m_nBitCountOut=0;//输出图像每像素位数为0      m_imgWidthOut=0;//输出图像的宽为0    m_imgHeightOut=0;//输出图像的高为0}/************************************************************************ 函数名称: GrayTrans()* 函数参数: CSize size -图像大小(宽、高)*            int nBitCount  -每像素比特数*            LPRGBQUAD lpColorTable  -颜色表指针*            unsigned char *pImgData  -位图数据指针* 返回值:   无* 说明:本函数为带参数的构造函数,给定位图的大小、每像素位数、颜色表*      及位图数据,调用ImgDib()对基类成员初始化,作为输入图像数据*      相关的数据成员,并初始化输出图像相关的数据成员***********************************************************************/GrayTrans::GrayTrans(CSize size, int nBitCount, LPRGBQUAD lpColorTable,                      unsigned char *pImgData):ImageDib(size, nBitCount, lpColorTable, pImgData){    m_pImgDataOut=NULL;//输出图像位图数据指针为空    m_lpColorTableOut=NULL;//输出图像颜色表指针为空    m_nColorTableLengthOut=0;//输出图像颜色表长度为0    m_nBitCountOut=0;//输出图像每像素位数为0    m_imgWidthOut=0;//输出图像的宽为0    m_imgHeightOut=0;//输出图像的高为0}/************************************************************************ 函数名称:  ~GrayTrans()* 说明:析构函数,释放资源***********************************************************************/GrayTrans::~GrayTrans(){    //释放输出图像位图数据缓冲区    if(m_pImgDataOut!=NULL){        delete []m_pImgDataOut;        m_pImgDataOut=NULL;    }    //释放输出图像颜色表    if(m_lpColorTableOut!=NULL){        delete []m_lpColorTableOut;        m_lpColorTableOut=NULL;    }}/************************************************************************ 函数名称: GetDimensions()* 函数参数:  无* 返回值:   图像的尺寸,用CSize类型表达* 说明:返回输出图像的宽和高***********************************************************************/CSize GrayTrans::GetDimensions(){       if(m_pImgDataOut == NULL) return CSize(0, 0);    return CSize(m_imgWidthOut, m_imgHeightOut);}/************************************************************************ 函数名称:  LinearStrech()* 函数参数:   CPoint point1,CPoint point2  -分段线性的对应点* 返回值:     无* 说明:分段线性拉伸函数,给定两个分段点,对m_pImgData所指向缓冲区中的灰度*      或彩色图像进行线性拉伸,为m_pImgDataOut申请内存,存放拉伸结果。***********************************************************************/void GrayTrans::LinearStrech(CPoint point1,CPoint point2){    //释放旧的输出图像缓冲区    if(m_pImgDataOut!=NULL){        delete []m_pImgDataOut;        m_pImgDataOut=NULL;    }    if(m_lpColorTableOut!=NULL){        delete []m_lpColorTableOut;        m_lpColorTableOut=NULL;    }    //输出图像每像素位数与输入图像相同    m_nBitCountOut=m_nBitCount;    //计算颜色表长度    m_nColorTableLengthOut=ComputeColorTabalLength(m_nBitCountOut);    //若有颜色表,输入图像的颜色表与输出图像颜色表相同    if(m_nColorTableLengthOut!=0){        m_lpColorTableOut=new RGBQUAD[m_nColorTableLengthOut];        memcpy(m_lpColorTableOut,m_lpColorTable,            sizeof(RGBQUAD)*m_nColorTableLengthOut);    }    //输出图像的宽高,与输入图像相等    m_imgWidthOut=m_imgWidth;    m_imgHeightOut=m_imgHeight;    //输出图像每行像素所占的字节数    int lineByteOut=(m_imgWidth*m_nBitCountOut/8+3)/4*4;    m_pImgDataOut=new unsigned char[lineByteOut*m_imgHeight];    if(m_nBitCount==8)//如果是灰度图像,则调用单通道数据线性拉伸函数        LinearStrechForSnglChannel(m_pImgData, m_pImgDataOut,                                     m_imgWidth, m_imgHeight,point1,point2);    else{        //若彩色图像,则把每个通道的数据进行分离,并线性拉伸,然后再合成为彩色图像        //单通道数据每行像素所占的字节数        int lineByteGray=(m_imgWidth+3)/4*4;        //临时缓冲区,存放单通道数据        unsigned char *bufIn=new unsigned char[m_imgHeight*lineByteGray];        //临时缓冲区,单通道数据拉伸的结果        unsigned char *bufOut=new unsigned char[m_imgHeight*lineByteGray];        //循环变量,图像坐标        int i, j;        //循环变量,遍历彩色图像的三个通道        int k;        for(k=0;k<3;k++){            //将每个通道数据提取出来,存入bufIn缓冲区            for(i=0;i<m_imgHeight;i++){                for(j=0;j<m_imgWidth;j++)                    bufIn[i*lineByteGray+j]=*(m_pImgData+i*lineByteOut+j*3+k);            }            //对bufIn缓冲区中的数据进行拉伸,拉伸结果存入bufOut中            LinearStrechForSnglChannel(bufIn,bufOut,m_imgWidth,m_imgHeight,point1,point2);            //将单通道拉伸的结果存入输出图像m_pImgDataOut对应通道中            for(i=0;i<m_imgHeight;i++){                for(j=0;j<m_imgWidth;j++)                    *(m_pImgDataOut+i*lineByteOut+j*3+k)=bufOut[i*lineByteGray+j];            }        }        //释放缓冲区        delete []bufIn;        delete []bufOut;    }}/************************************************************************ 函数名称:  LinearStrechForSnglChannel()* 函数参数:   unsigned char *pImgDataIn   -待拉伸的位图数据指针*             unsigned char *pImgDataOut  -拉伸后位图数据指针*             int imgWidth   -位图宽,像素为单位*             int imgHeight  -位图高,像素为单位*             CPoint point1,point2 -分段线性的两个转折点坐标* 返回值:     无* 说明:给定一个通道的数据,以及两个转折点坐标,做分段线性拉伸, 将结果*     存入pImgDataOut所指向的缓冲区。***********************************************************************/void GrayTrans::LinearStrechForSnglChannel(unsigned char *pImgDataIn,         unsigned char *pImgDataOut, int imgWidth,int imgHeight,CPoint point1,CPoint point2){    //循环变量,图像坐标    int i,j;    //每行像素的字节数,单通道图像    int lineByte=(imgWidth+3)/4*4;    //(x1,y1)和(x2,y2)为两个分段点的坐标    double x1=point1.x,y1=point1.y,x2=point2.x, y2=point2.y;    //三个分段的直线斜率    double slop1=y1/x1,slop2=(y2-y1)/(x2-x1),slop3=(255-y2)/(255-x2);    //三个直线坐标的斜率    double dis1=0,dis2=y1-slop2*x1,dis3=y2-slop3*x2;    //映射表    int Map[256];    //根据三个分段,对输入图像每个灰度级计算映射表    for(i=0;i<256;i++)      {        if(i<x1)            Map[i]=(int)(slop1*i+dis1+0.5);        else if(i<x2)            Map[i]=(int)(slop2*i+dis2+0.5);        else            Map[i]=(int)(slop3*i+dis3+0.5);    }    //临时变量    int temp;    //对每个像素,根据输入像素灰度级查找映射表,并为输出像素赋值    for(i=0;i<imgHeight;i++)    {        for(j=0;j<lineByte;j++)        {            //输入图像(i,j)像素的灰度级            temp=*(pImgDataIn+i*lineByte+j);            //根据映射表为输出图像赋值            *(pImgDataOut+i*lineByte+j)=Map[temp];        }    }}void GrayTrans::RevImage(){    //释放旧的输出图像数据及颜色表缓冲区    if(m_pImgDataOut!=NULL){        delete []m_pImgDataOut;        m_pImgDataOut=NULL;    }    if(m_lpColorTableOut!=NULL){        delete []m_lpColorTableOut;        m_lpColorTableOut=NULL;    }    //输出图像每像素位数与原图相同    m_nBitCountOut=m_nBitCount;    //颜色表长度    m_nColorTableLengthOut=ComputeColorTabalLength(m_nBitCountOut);    //如果有颜色表,则为颜色表分配内存    if(m_nColorTableLengthOut!=0){        m_lpColorTableOut=new RGBQUAD[m_nColorTableLengthOut];        memcpy(m_lpColorTableOut,m_lpColorTable,sizeof(RGBQUAD)*m_nColorTableLengthOut);    }    //输出图像的宽高,与输入图像相等    m_imgWidthOut=m_imgWidth;    m_imgHeightOut=m_imgHeight;    //每行像素占字节数,必须为4的倍数    int lineByteOut=(m_imgWidth*m_nBitCountOut/8+3)/4*4;    //申请空间,存放变换后结果    m_pImgDataOut=new unsigned char[lineByteOut*m_imgHeight];    //循环变量,图像的坐标    int i,j;    //每像素占字节数    int pixelByte=m_nBitCountOut/8;    //循环变量,遍历每个像素的每个分量,比如彩色图像三个分量    int k;    //求负相    for(i=0;i<m_imgHeight;i++){        for(j=0;j<m_imgWidth;j++){            for(k=0;k<pixelByte;k++)                *(m_pImgDataOut+i*lineByteOut+j*pixelByte+k)                =255-*(m_pImgData+i*lineByteOut+j*pixelByte+k);        }    }}/***************************************************************************函数名    BinaryImage()*参数名    int threshold   -阈值*返回值    无*说明:该函数将m_pImgData作为输入图像,根据给定的阈值threshold对图像进行二值化,*      结果存入m_pImgDataOut中。若是彩色图像,则先将图像进行灰值图像后再二值化。***************************************************************************/void GrayTrans::BinaryImage(int threshold){    //对于灰度图像    if(m_nBitCount==8){        //释放旧的输出图像数据及颜色表缓冲区        if(m_pImgDataOut!=NULL){            delete []m_pImgDataOut;            m_pImgDataOut=NULL;        }        if(m_lpColorTableOut!=NULL){            delete []m_lpColorTableOut;            m_lpColorTableOut=NULL;        }        //输出图像的每像素位数、颜色表长度        m_nBitCountOut=m_nBitCount;        m_nColorTableLengthOut=ComputeColorTabalLength(m_nBitCountOut);        //申请输出图像颜色表缓冲区,并将输入图像颜色表拷贝至输出图像颜色表中        m_lpColorTableOut=new RGBQUAD[m_nColorTableLengthOut];        memcpy(m_lpColorTableOut,m_lpColorTable,sizeof(RGBQUAD)*m_nColorTableLengthOut);        //输出图像的宽高,与输入图像相等        m_imgWidthOut=m_imgWidth;        m_imgHeightOut=m_imgHeight;        //图像每行像素所占字节数,输入图像与输出图像每行像素所占字节数相等        int lineByte=(m_imgWidth*m_nBitCount/8+3)/4*4;        //申请输出图像位图数据缓冲区        m_pImgDataOut=new unsigned char[lineByte*m_imgHeight];        //循环变量,图像的坐标        int i,j;        //二值化        for(i=0;i<m_imgHeight;i++){            for(j=0;j<m_imgWidth;j++){                if(*(m_pImgData+i*lineByte+j)<threshold)                    *(m_pImgDataOut+i*lineByte+j)=0;                else                    *(m_pImgDataOut+i*lineByte+j)=255;            }        }    }    else    {        if(m_pImgDataOut!=NULL){            delete []m_pImgDataOut;            m_pImgDataOut=NULL;        }        if(m_lpColorTableOut!=NULL){            delete []m_lpColorTableOut;            m_lpColorTableOut=NULL;        }        //灰值化后,每像素位数为8比特        m_nBitCountOut=8;        //颜色表长度        m_nColorTableLengthOut=ComputeColorTabalLength(m_nBitCountOut);        //申请颜色表缓冲区,生成灰度图像的颜色表        if(m_nColorTableLengthOut!=0){            m_lpColorTableOut=new RGBQUAD[m_nColorTableLengthOut];            for(int i=0; i<m_nColorTableLengthOut;i++){                m_lpColorTableOut[i].rgbBlue=i;                m_lpColorTableOut[i].rgbGreen=i;                m_lpColorTableOut[i].rgbRed=i;                m_lpColorTableOut[i].rgbReserved=0;            }        }        //输入图像每像素字节数,彩色图像为3字节/像素        int pixelByteIn=3;        //输入图像每行像素所占字节数,必须是4的倍数        int lineByteIn=(m_imgWidth*pixelByteIn+3)/4*4;        //输出图像的宽高,与输入图像相等        m_imgWidthOut=m_imgWidth;        m_imgHeightOut=m_imgHeight;        //输出图像每行像素所占字节数,必须是4的倍数        int lineByteOut=(m_imgWidth*m_nBitCountOut/8+3)/4*4;        //申请输出图像位图数据缓冲区        m_pImgDataOut=new unsigned char[lineByteOut*m_imgHeight];        //循环变量,图像的坐标        int m,n;        //根据灰值化公式为输出图像赋值        for(m=0;m<m_imgHeight;m++){            for(n=0;n<m_imgWidth;n++)                *(m_pImgDataOut+m*lineByteOut+n)=0.11**(m_pImgData+m*lineByteIn+n*pixelByteIn+0)                +0.59**(m_pImgData+m*lineByteIn+n*pixelByteIn+1)                +0.30**(m_pImgData+m*lineByteIn+n*pixelByteIn+2)+0.5;    }        //输出图像每行像素所占字节数,输入图像与输出图像每行像素所占字节数不等        int i,j;        //二值化        for(i=0;i<m_imgHeight;i++){            for(j=0;j<m_imgWidth;j++)                if(*(m_pImgDataOut+i*lineByteOut+j)<threshold)                    *(m_pImgDataOut+i*lineByteOut+j)=0;                else                    *(m_pImgDataOut+i*lineByteOut+j)=255;        }    }}void GrayTrans::ThresholdWindow(int bBottom, int bTop ){    //释放旧的输出图像数据及颜色表缓冲区    if(m_pImgDataOut!=NULL){        delete []m_pImgDataOut;        m_pImgDataOut=NULL;    }    if(m_lpColorTableOut!=NULL){        delete []m_lpColorTableOut;        m_lpColorTableOut=NULL;    }    //输出图像的每像素位数、颜色表长度    m_nBitCountOut=m_nBitCount;    m_nColorTableLengthOut=ComputeColorTabalLength(m_nBitCountOut);    //申请输出图像颜色表缓冲区,并将输入图像颜色表拷贝至输出图像颜色表中    m_lpColorTableOut=new RGBQUAD[m_nColorTableLengthOut];    memcpy(m_lpColorTableOut,m_lpColorTable,sizeof(RGBQUAD)*m_nColorTableLengthOut);    //输出图像的宽高,与输入图像相等    m_imgWidthOut=m_imgWidth;    m_imgHeightOut=m_imgHeight;    //图像每行像素所占字节数,输入图像与输出图像每行像素所占字节数相等    int lineByte=(m_imgWidthOut*m_nBitCountOut/8+3)/4*4;    //申请输出图像位图数据缓冲区    m_pImgDataOut=new unsigned char[lineByte*m_imgHeightOut];    int pixelByte=m_nBitCountOut/8;    //循环变量,图像的坐标    int i,j,k;    //窗口变换    for(i=0;i<m_imgHeightOut ;i++){        for(j=0;j<m_imgWidthOut;j++){            for(k=0;k<pixelByte;k++){                *(m_pImgDataOut+i*lineByte+j*pixelByte+k)=*(m_pImgData+i*lineByte+j*pixelByte+k);            if(*(m_pImgData+i*lineByte+j*pixelByte+k)<bBottom)                *(m_pImgDataOut+i*lineByte+j*pixelByte+k)=0;            else if (*(m_pImgData+i*lineByte+j*pixelByte+k)>bTop)                 *(m_pImgDataOut+i*lineByte+j*pixelByte+k)=255;            }        }    }}

我已经把其他几个灰度变换中的功能函数写在里边了。

step4.映射函数和相应函数,如下图:
这里写图片描述
代码如下:

afx_msg void OnBinary();

这里写图片描述
代码如下:

void Cdemo1View::OnBinary() {    // TODO: Add your command handler code here    //获取文档类中m_dib的指针,访问当前DIB数据    Cdemo1Doc *pDoc=GetDocument();    ImageDib *pDib=pDoc->m_dib;    //异常判断    if(pDib->m_nBitCount!=24&&pDib->m_nBitCount!=8){        ::MessageBox(0,L"只处理彩色和灰度图像",MB_OK,0);        return ;    }    //将pDib中的图像数据作为输入数据,调用带参数的构造函数,    //定义GrayTrans类的对象graytrans    GrayTrans graytrans(pDib->GetDimensions(),pDib->m_nBitCount,        pDib->m_lpColorTable, pDib->m_pImgData);    //调用Binary()对图像进行二值化,缺省状态下阈值为    graytrans.BinaryImage();    //建立一个新视图,显示分割结果    CMainFrame* pFrame = (CMainFrame *)(AfxGetApp()->m_pMainWnd);    pFrame->SendMessage(WM_COMMAND, ID_FILE_NEW);    Cdemo1View* pView=(Cdemo1View*)pFrame->MDIGetActive()->GetActiveView();    Cdemo1Doc* pDocNew=pView->GetDocument();    ImageDib *dibNew=pDocNew->m_dib;    dibNew->ReplaceDib(graytrans.GetDimensions(),graytrans.m_nBitCountOut,graytrans.m_lpColorTableOut, graytrans.m_pImgDataOut);    pView->OnInitialUpdate();       pDocNew->SetModifiedFlag(TRUE);    pDocNew->UpdateAllViews(pView);    Invalidate();}

注意导入GrayTrans.h这个头文件,如下图:
这里写图片描述

运行如下图所示:
首先打开显示一幅BMP照片
这里写图片描述

二值化后的结果如下:
这里写图片描述

ok,码完收工。

0 0
原创粉丝点击