图像基础操作(含代码)

打开BMP格式图像文件

BMP文件分为BITMAPFILEHEADER、BITMAPINFORHEADER、RGBQUAD三部分文件头BF包含文件的类型,文件的大小,位图数据距文件头的偏移量等,BI是说明位图的信息,有位图的颜色位数biBitCount,位图的高度宽度,以及位图数据的大小,通过读取BMP格式文件的这些信息,就能对其进行解码,打开BMP文件。

例程:

//选取文件
LPCTSTR lpszFilter = "BMP Files(*.bmp)|*.bmp|任何文件|*.*||";
CFileDialog dlg1(TRUE,lpszFilter,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,lpszFilter,NULL);
CString filename;
CFile file;
if(dlg1.DoModal()==IDOK)
{
//读入文件
filename = dlg1.GetPathName();
if(file.Open(filename,CFile::modeRead|CFile::shareDenyNone,NULL)==0)
{
AfxMessageBox("无法打开文件",MB_OK,0);
    return;
}

//读取文件头,将大小为sizeof(bf)的数据传入缓冲区bf
file.Read(&bf,sizeof(bf));
        //判断是否是BMP文件
if(bf.bfType!=0x4d42)
{
AfxMessageBox("非BMP文件!",MB_OK,0);
    return;
}
        //判断文件是否损坏
if(file.GetLength()!=bf.bfSize)
{
AfxMessageBox("文件已损坏,请检查!");
    return;
}

//读取信息头
file.Read(&bi,sizeof(bi));
//计算调色板数目
numQuad = 0;
if(bi.biBitCount < 24)
{
//如果为1,4,8,则1向左移动对应的位数,对应的调色板数目为2,16,256
numQuad = 1<<bi.biBitCount;
}
    //为图像信息pbi申请空间
pbi = (BITMAPINFO*)HeapAlloc(GetProcessHeap(),0,sizeof(BITMAPINFOHEADER)+numQuad*sizeof(RGBQUAD));
        memcpy(pbi,&bi,sizeof(bi));
quad = (RGBQUAD*)((BYTE*)pbi+sizeof(BITMAPINFOHEADER));
        //读取调色板
        if(numQuad!=0)
{
file.Read(quad,sizeof(RGBQUAD)*numQuad);
}
//为图像数据申请空间
bi.biSizeImage = bf.bfSize - bf.bfOffBits;
lpBuf = (BYTE*)HeapAlloc(GetProcessHeap(),0,bi.biSizeImage);
hTempBuf=LocalAlloc(LHND,bi.biSizeImage);
lpTempBuf=(BYTE*)LocalLock(hTempBuf);
//      LONG lLineBytes;
//	    lLineBytes =WIDTHBYTES(3*lWidth*8); //计算每行的字节数
//	 bi.biHeight*lLineBytes
//读取图像数据
file.Read(lpBuf,bi.biSizeImage);
//图像读取完毕,关闭文件,设置标志
memcpy(lpTempBuf,lpBuf,bi.biSizeImage);
file.Close();
flag = 1;

}

灰度处理(黑白效果):

图像灰度化就是使色彩的三种颜色分量R、G、B的值相同,由于颜色值的取值范围是[0,255],所以灰度的级别只有256种,即灰度图象仅能表现256种灰度颜色,常用有3种处理方法:

*最大值法(Maximum):R=G=B=Max(R,G,B),这种方法处理后灰度图象的亮度会偏高。

*平均值法(Average):R=G=B=(R+G+B)/3,这种方法处理后灰度图象的亮度较柔和。

*加权平均值法(Weighted Average):

R=G=B=wr*R+wg*G+wb*B,wr、wg、wb分别为R、G、B的权值。当其权值取不同的值时,能够形成不同灰度的灰度图象,由于人眼对绿色的敏感度最高,红色次之,蓝色最低,因此当wg > wr > wb时,所产生的灰度图像更符合人眼的视觉感受。通常wr=30%,wg=59%,wb=11%,图像的灰度最合理。

例程:

CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf);     //图像数据起始位置的指针
LONG lWidth = pDoc->bi.biWidth;     //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight;   //源图像宽度,像素数

unsigned char* lpSrc;           //某个像素对应的指针
int gray;            //灰色对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(3*lWidth*8); //计算每行的字节数

for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+3*j;
gray = ((*lpSrc)*11+(*(lpSrc+1))*59+(*(lpSrc+2))*30)/100;
*lpSrc = gray;
*(lpSrc+1)= gray;
*(lpSrc+2) = gray;
}
}
bGray = 1;
CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf);     //图像数据起始位置的指针
LONG lWidth = pDoc->bi.biWidth;     //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight;   //源图像宽度,像素数

unsigned char* lpSrc;           //某个像素对应的指针
int gray;            //灰色对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(3*lWidth*8); //计算每行的字节数

for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+3*j;
gray = ((*lpSrc)*11+(*(lpSrc+1))*59+(*(lpSrc+2))*30)/100;
*lpSrc = gray;
*(lpSrc+1)= gray;
*(lpSrc+2) = gray;
}
}
bGray = 1;

灰度拉伸:

属于图像增强技术,如果一幅图像的灰度集中在较暗的区域而导致图像偏暗,可以用灰度拉伸功能来拉伸物体灰度区间以改善图像;同样如果图像灰度集中在较亮的区域而导致图像偏亮,也可以用灰度拉伸功能来压缩物体灰度区间以改善图像质量

我以拉伸物体灰度区间为例,将灰度在a以下的像素灰度变为0,灰度在b以上的像素变为255,然后将本来在a和b之间的像素调整到0-255。

即设灰度值为gray,则:            0                              gray < a

gray  =   (*lpSrc-low_value)*rate+c         a<gray<b

255                             gray>b

其中low_value、high_value、rate、c由自己设定,若由low_value~high_value变为0~255,c取0.5用来进行四舍五入, rate = (255-0+1)/(high_value-low_value+1)

例程:该例程将灰度值靠近0的10%设为0,将靠近255的10%设为255,其他进行灰度拉伸

CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf);     //图像数据起始位置的指针
LONG lWidth = (pDoc->bi.biWidth)*3;     //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight;   //源图像宽度,像素数

unsigned char* lpSrc;           //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数

BYTE bMap[256];            //存放灰度拉伸后的灰度值
    float rate=0;               

int temp;
float stretch_num[256];    //存放各个灰度级出现的次数
    float stretch_p[256];      //各个灰度级出现的比率
    float stretch_sum[256];    //求存放各个灰度级之前的概率和
//清空三个数组
memset(stretch_p,0,sizeof(stretch_p));
memset(stretch_sum,0,sizeof(stretch_sum));
memset(stretch_num,0,sizeof(stretch_num));
int low_value,high_value;
//求存放图象各个灰度级出现的次数
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
stretch_num[*lpSrc]++;
}
}
//求存放图像各个灰度级的出现概率
for(i=0;i<256;i++)
{
stretch_p[i]=stretch_num[i]/(lWidth*lHeight);
}
//求存放各个灰度级之前的概率和
for(i=0;i<256;i++)
{
for(j=0;j<=i;j++)
{
            stretch_sum[i]+=stretch_p[j];
}
}
//统计出两个阈值点的值
for(i=0;i<256;i++)
{
if(stretch_sum[i]<0.1) //low_value 取接近10%的总像素的灰度值
{
        low_value=i;
}
if(stretch_sum[i]>0.9) //high_value取接近90%的总像素的灰度值
{
high_value=i;
break;
}
}

rate=(float)256/(high_value-low_value+1);
    //进行灰度拉伸
for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
if(*lpSrc<low_value)
{
*lpSrc = 0;
}
else if(*lpSrc>high_value)
{
                *lpSrc = 255;
}
else
{
                temp=((*lpSrc-low_value)*rate)+0.5;
if(temp<=255)
{
                    *lpSrc = temp;
}
else
{
                    *lpSrc = 255;
}

}
}
}

图像腐蚀:

对形态学结构元素B进行z平移后,如果结构元素全部包含于集合A中,则z属于腐蚀后的集合。这个定义的意思就是从图像的第一个像素点开始依行遍历全部像素,在每个像素点上,判断是否结构元素全部位于集合A内,如果是则该点属于腐蚀后的集合,需要保留这个点,否则对改点取反(按照下面的符号约定,即将改点灰度值设置为0)。

若对黑纸上的白字进行图像腐蚀,则只针对白色的范围内,若左边或右边是黑色,则将其设置为黑色,否则不变。

例程:

CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf);     //图像数据起始位置的指针
    LPSTR lpTempDIB =(LPSTR)GlobalLock(pDoc->lpTempBuf);
LONG lWidth = (pDoc->bi.biWidth)*3;     //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight;   //源图像宽度,像素数

BYTE *lpSrc,*lpTempSrc;           //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数

memcpy(pDoc->lpTempBuf,pDoc->lpBuf,pDoc->bi.biSizeImage);
    //在水平方向进行腐蚀运算
    for(i=0;i<lHeight;i++)
{
        for(j=3;j<lWidth-7;j=j+3)//注意为防止越界,j的范围从1到(宽度-2)
{
//lpSrc指向原图数据
            lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
lpTempSrc = (unsigned char*)lpTempDIB+lLineBytes*(lHeight-1-i)+j;
            if (*lpTempSrc==255)
{
                for(int x=0;x<7;x=x+3)
{
                    if((*(lpTempSrc+x-3))!=255)
{
                        //自身及左右邻居中若有一个不是白点,则将该点腐蚀
                        *lpSrc=*(lpTempSrc+x-3);
                        *(lpSrc+1)=*(lpTempSrc+x-3);
                        *(lpSrc+2)=*(lpTempSrc+x-3);
                        break;
}
}
}
}
}

图像相减:

在图像处理中,图像相减也是比较常用的一个概念,比如把图像和背景图像相减就可以得到物体的图像;把图像和腐蚀后的图像相减就可以得到物体的轮廓。。图像相减即对应像素的挨个相减,比较简单。

例程:

CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf);     //图像数据起始位置的指针
LPSTR lpTempDIB =(LPSTR)GlobalLock(pDoc->lpTempBuf);
LONG lWidth = (pDoc->bi.biWidth)*3;     //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight;   //源图像宽度,像素数

unsigned char* lpSrc,*lpTempSrc;           //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数

for(i=0;i<lHeight;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
lpTempSrc = (unsigned char*)lpTempDIB+lLineBytes*(lHeight-1-i)+j;
*lpSrc = *lpSrc - *lpTempSrc;
}
}

中值滤波:

在图像处理中,经常会遇到各种噪声(即不需要的像素点),中值滤波就是舍去不需要的像素点,保留需要像素点的一种方法。它将待处理像素点上下的五个像素点保存到一个数组中(y-2,y-1,y,y+1,y+2),比较其大小,将其排序,并取中值为该点的像素值,通过该方法能去掉突出的像素点。

例程:

CPictureDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//将lpBuf的指针复制给lpDIBBits
LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf);     //图像数据起始位置的指针
LPSTR lpTempDIB = (LPSTR)GlobalLock(pDoc->lpTempBuf);
LONG lWidth = (pDoc->bi.biWidth)*3;     //源图像宽度,像素数
LONG lHeight = pDoc->bi.biHeight;   //源图像宽度,像素数

unsigned char *lpSrc,*lpTempSrc;           //某个像素对应的指针
LONG i,j,lLineBytes;
lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数

int pFilter_Image_Pixel[5];//窗口像素值
    int mid_pixel_value=0;  // 中值
int flag;
int temp=0;// 中间变量
//中值滤波
memcpy(pDoc->lpTempBuf,pDoc->lpBuf,pDoc->bi.biSizeImage);
for(i=2;i<lHeight-2;i++)
{
for(j=0;j<lWidth;j++)
{
lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
            lpTempSrc = (unsigned char*)lpTempDIB+lLineBytes*(lHeight-1-i)+j;
//把5*1屏蔽窗口的所有像素值放入pFilter_Image_Pixel[m]
int m=0;
for(int y=-2;y<=2;y++)
{
pFilter_Image_Pixel[m]=*(lpTempSrc-lLineBytes*y);
m++;
}
//把pFilter_Image_Pixel[m]中的值按降序排列
do{
flag=0;
for(int m=0;m<4;m++)
{
if(pFilter_Image_Pixel[m]<pFilter_Image_Pixel[m+1])
{
temp=pFilter_Image_Pixel[m];
pFilter_Image_Pixel[m]=pFilter_Image_Pixel[m+1];
pFilter_Image_Pixel[m+1]=temp;
flag=1;
}
     }
   }while(flag==1);

mid_pixel_value=pFilter_Image_Pixel[2];//求中值mid_pixel_value
*lpSrc=mid_pixel_value;//将中值赋给目标图像的当前点
}
}

二值化:

二值化是阈值变换的一种方法,即设定一个阈值,该点像素大于该阈值则设其为255,小于该阈值设为0,通过二值化使图像更加方便被提取特征。

例程:

if(bGray == 0)
{
AfxMessageBox("请先进行灰度处理",MB_OK);
}
else{
        CPictureDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    LPSTR lpDIBBits =(LPSTR)GlobalLock(pDoc->lpBuf);     //图像数据起始位置的指针
    LONG lWidth = (pDoc->bi.biWidth)*3;;     //源图像宽度
    LONG lHeight = pDoc->bi.biHeight;   //源图像宽度
        BYTE bThre = 125;  //阈值

        unsigned char* lpSrc;           //某个像素对应的指针
    LONG i,j,lLineBytes;
    lLineBytes =WIDTHBYTES(lWidth*8); //计算每行的字节数
        for(i=0;i<lHeight;i++)
{
    for(j=0;j<lWidth;j++)
{
    lpSrc = (unsigned char*)lpDIBBits+lLineBytes*(lHeight-1-i)+j;
    	if((*lpSrc)<bThre)
{
        *lpSrc = 0;
}else
{
    	*lpSrc = 255;
}
}
}
}

原图:

灰度处理后:

灰度拉伸后:

腐蚀后:

相减后:

二值化后:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-07 00:09:16

图像基础操作(含代码)的相关文章

数据结构-线性表的一些基础操作 c++代码

//线性表的顺序存储结构 template <class T> class Linearlist { public: Linearlist(int MaxListSize == 10); ~Linearlist() { delete []element; } bool IsEmpty() const { return length == 0; } bool IsFull() const { return length == MaxSize; } int Length() const { ret

数据结构-栈的一些基础操作c++代码

堆栈(简称栈) 是一种操作受限的线性表,只允许在表的同一端进行插入和删除操作,且这些操作是按先进后出的原则进行的. template <class T> struct SLNode { T data; //数据域 SLNode<T> *next; //指针域 SLNode(SLNode *nextNode = NULL) //构造函数 { next = nextNode; } SLNode(const T &item, SLNode *nextNode = NULL) //

【ADO.NET基础-GridView】GridView的编辑、更新、取消、删除以及相关基础操作代码

代码都是基础操作,后续功能还会更新,如有问题欢迎提出和提问....... 前台代码: <asp:GridView ID="GridView1" runat="server" BackColor="White" BorderColor="White" BorderStyle="Ridge" BorderWidth="2px" AutoGenerateColumns="Fal

OpenCV基础(二)---图像像素操作

图像像素操作 通过访问图像的像素,对灰度图像和RGB图像进行反差. 例如,在灰度图像中,某一个像素值为,pixel_value. 那么反差后的pixel_value = 255 - pixel_value. 对RGB图像处理类似,差别在于要对每一个颜色通道进行反差. 方法1: 1 #include <opencv2/opencv.hpp> 2 #include <iostream> 3 4 using namespace cv; 5 using namespace std; 6 7

分布式版本管理器Git的基础操作与配置指南&lt;二&gt;

一.Git分支介绍与创建 Git中的分支,其实本质上仅仅是个指向commit对象的可变指针.Git回使用master作为分支的默认名 字,在若干次提交后,你其实已经有了一个指向最后一次提交对象的master分支,它在每次提交的时候 都会自动向前移动. 1.Git分支的创建,切换和删除 git branch 不带参数,它会给出当前所有分支的清单,*表示当前所在的分支 git branch -v 附带显示各个分支最后一次commit信息 git branch testing 创建一个tesing分支

WinForm之BindingSource基础操作实例教程

通常我们在进行数据绑定的时候,常用的数据源有DataSet.DataTable.BindingList<T>.还有强类型数据源.今天我们来通过实例了解一下BindingSource组建,分享给大家供大家参考借鉴之用. BindingSource的两个用途: (1)首先,它提供一个将窗体上的控件绑定到数据的间接层.这是通过将 BindingSource 组件绑定到数据源,然后将窗体上的控件绑定到 BindingSource 组件来完成的.与数据的所有进一步交互(包括导航.排序.筛选和更新)都是通

C#基础—不安全代码(unsafe code)

1.为何要有unsafe 也许是为了实现CLR类型安全的目标吧,默认情况下,C#没有提供指针的使用算法,但是有些情况下也可能需要指针这样直接访问内存的东西(虽然目前我还没有用过),但是有时候程序员非常清楚程序的运行状况,需要使用指针直接访问内存以便于提高性能或者调试.监控程序运行的内存的使用状况,以便于采取相应的措施.还有一些情况是当我们需要调用外面DLL中的函数又不能使用DllImport 时,也需要指针来传递这些函数. 2.unsafe 的定义     MSDN:unsafe 关键字表示不安

php之文件基础操作

在php中对文件的基础操作非常的简单,php提供的函数粗略的用了一遍. file_get_contents():可以获取文件的内容获取一个网络资源的内容,这是php给我封装的一个比较快捷的读取文件的内容.网络资源的函数,此函数里面封装了对文件的打开,读取,关闭操作.一次性的将内容全部读取到内存中,相当方便,但是对于大文件或者网络资源较大的时候,不建议使用.file_put_contents():写入数据,和file_get_contents()类似. 文件的基础操作:touch()--新建,fo

Mac下Git的基础操作

目前最火的版本控制软件是Git了吧,今天简单梳理一下Mac下Git的基础操作~~ 一.什么是Git Git是一个分布式代码管理工具,用于敏捷的处理或大或小的项目,类似的工具还有svn. 基于Git的快速的.免费的.稳定的在线代码托管平台有github,还有一些国内的,coding.csdn代码托管平台.京东代码托管平台等等. 二.Git的基本使用 1.注册一个git账号 咱们以coding代码托管平台为例来简单介绍 A.登陆coding网站https://coding.net/,注册coding