【C】用C语言提取bmp图片像素,并进行K-means聚类分析——容易遇到的问题
关于bmp图片的格式,网上有很多文章,具体可以参考百度百科,也有例子程序。这里只提要注意的问题。
(1)结构体定义问题:首先按照百度百科介绍的定义了结构体,但是编译发现重定义BITMAPFILEHEADER等。其实只要包含了Windows.h,里面的wingdi.h就已经定义了处理bmp的结构体,故不需要自己再重复定义。
(2)读取文件的字节对其问题:要使用#pragma pack (1)来方便读取文件头的结构体,否则结构体的大小会由于字节对齐问题改变。不知是否头文件中已经使用了该宏,在我的代码中注释掉#pragma pack (1)也可以正确运行。另外百度到“pack提供数据声明级别的控制,对定义不起作用”,自己也不太清楚这个宏用在哪里比较合适,一般见是在定义结构体的时候,还请各位批评指正。
(3)补齐行数问题:在看百科介绍结构体时,BITMAPINFOHEADER的biSizeImage表示“位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位”,并且有相关的计算方法 。我要强调的是提取像素时要排除这些补齐用字节的影响。按照百度百科上提取像素的方法是会将这些补齐用的00字节算入在内的,从而影响后面的算法。
博客园无法上传bmp图片,所以不贴效果图了。有何问题欢迎批评指正
下面是C语言代码供参考:
#pragma once #include "targetver.h" #include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include "bitmap.h"
#include <math.h>
bitmap.h:
#pragma pack (1)//字节对齐的控制!非常注意!
/*
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;//位图文件的类型,必须为BM(1-2字节)
DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)
//文件头的偏移量表示,以字节为单位
}BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//本结构所占用字节数(15-18字节)
LONG biWidth;//位图的宽度,以像素为单位(19-22字节)
LONG biHeight;//位图的高度,以像素为单位(23-26字节)
WORD biPlanes;//目标设备的级别,必须为1(27-28字节)
WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节)
//4(16色),8(256色)16(高彩色)或24(真彩色)之一
DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)
}BITMAPINFOHEADER; typedef struct tagRGBQUAD{
BYTE rgbBlue;//蓝色的亮度(值范围为0-255)
BYTE rgbGreen;//绿色的亮度(值范围为0-255)
BYTE rgbRed;//红色的亮度(值范围为0-255)
BYTE rgbReserved;//保留,必须为0
}RGBQUAD;
*/
typedef struct
{
BYTE b;
BYTE g;
BYTE r;
}RGB; //带有坐标的颜色RGB表示
typedef struct
{
RGB rgb;
int height;
int width;
} RGB_EX;
#pragma pack ()//字节对齐的控制
main.c:
// 针对图片实现K-means聚类算法.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h" float distance(RGB x, RGB mean);
int kmeans_img(RGB **Img, LONG ImgWidth, LONG ImgHeight, ULONG lCount, USHORT K); int _tmain(int argc, _TCHAR* argv[])
{
//#pragma pack (1)//字节对齐的控制!非常注意!
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
FILE* pfin; fopen_s(&pfin, "test2.bmp","rb");
FILE* pfout; fopen_s(&pfout, "ouput.bmp","wb");
//ReadtheBitmapfileheader;
fread(&fileHeader, sizeof(BITMAPFILEHEADER), , pfin);
//ReadtheBitmapinfoheader;
fread(&infoHeader, sizeof(BITMAPINFOHEADER), , pfin);
//为简化代码,只处理24位彩色
if(infoHeader.biBitCount==) {
int size = infoHeader.biWidth * infoHeader.biHeight;
RGB **ppImg = NULL;
int r;
//开辟空间并读入图片
//RGB img[infoHeader.biHeight][infoHeader.biWidth]; //这里有错误,尺度改为常亮
//fread(ppImg, sizeof(RGB), size, pfin);
ppImg = (RGB**)malloc(infoHeader.biHeight * sizeof(RGB*));
if (!ppImg)
return -;
//注意!需要处理补齐字节问题:每行字节数目必须是4的整数倍
r = infoHeader.biWidth % ;
for (int i = ; i < infoHeader.biHeight; i++) {
ppImg[i] = (RGB*)malloc(sizeof(RGB) * infoHeader.biWidth);
if (ppImg[i]) {
fread(ppImg[i], sizeof(RGB), infoHeader.biWidth, pfin);
fseek(pfin, r, SEEK_CUR);
}
else
return -;
} /*
//把第50行染成黑色
int i=0;
for(;i<infoHeader.biWidth;i++) {
ppImg[50][i].b = ppImg[50][i].g = ppImg[50][i].r = 0;
}
*/ kmeans_img(ppImg, infoHeader.biWidth, infoHeader.biHeight, , ); //将修改后的图片保存到文件
fileHeader.bfSize = infoHeader.biHeight * infoHeader.biWidth * + fileHeader.bfOffBits;
fwrite(&fileHeader,sizeof(fileHeader),,pfout);
fwrite(&infoHeader,sizeof(infoHeader),,pfout);
for (int i = ; i < infoHeader.biHeight; i++) {
fwrite(ppImg[i],sizeof(RGB),infoHeader.biWidth,pfout);
int temp = r;
while (temp--)
{
fputc(, pfout);
}
} //释放图片占用内存
for (int i = ; i < infoHeader.biHeight; i++)
free(ppImg[i]);
free(ppImg);
}
fclose(pfin);
fclose(pfout);
//#pragma pack ()
return ;
} /*
对图片像素使用K-means算法聚类,聚成K类
Img:RGB矩阵形式的图片。第一维是高度Height。Img[ImgHeight][ImgWidth]。
为保证算法正确性,图片中应已经剔除了补齐字节用的00
ImgWidth:图片宽
ImgHeight:图片高
lCount:迭代次数
K:聚类数目 */
int kmeans_img(RGB **Img, LONG ImgWidth, LONG ImgHeight, ULONG lCount, USHORT K)
{
int iFlag;//收敛后置为0
RGB *means = (RGB*)malloc(K * sizeof(RGB));//K个中心
RGB_EX **Cluster = NULL;//存放簇
int *ClusterLength = NULL;
Cluster = (RGB_EX**)malloc(K * sizeof(RGB_EX*));
ClusterLength = (int *)malloc(K * sizeof(int));
for (int i = ; i < K; i++) {
//随意指定K个中心,应该还有更好的算法.
means[i] = Img[(ImgHeight/(i+))-][(ImgWidth/(i+))-];
//开辟簇的存储空间
Cluster[i] = (RGB_EX*)malloc(ImgHeight * ImgWidth * sizeof(RGB_EX));
} iFlag = K;
//开始迭代
while (lCount-- && iFlag)
{
iFlag = K;
//每次聚类前要初始化
for (int i = ; i < K; i++)
ClusterLength[i] = ; //对每个像素循环,归置到相应的簇里
for (int i = ; i < ImgHeight; i++) {
for (int j = ; j < ImgWidth; j++) {
int iClusterIndex = ;
float fMinDistance = * + * + * ;
float d = ;
for (int k = ; k < K; k++) {
d = distance(Img[i][j], means[k]);
fMinDistance = fMinDistance > d ? iClusterIndex = k, d : fMinDistance;
}
Cluster[iClusterIndex][ClusterLength[iClusterIndex]].rgb = Img[i][j];
Cluster[iClusterIndex][ClusterLength[iClusterIndex]].height = i;
Cluster[iClusterIndex][ClusterLength[iClusterIndex]++].width = j;
}
} //重新计算每个簇的均值
for (int i = ; i < K; i++) {
unsigned long sumR = , sumG = , sumB = ;
BYTE R = , G = , B = ;
for (int j = ; j < ClusterLength[i]; j++) {
sumR += Cluster[i][j].rgb.r;
sumG += Cluster[i][j].rgb.g;
sumB += Cluster[i][j].rgb.b;
}
if (ClusterLength[i]) {
R = sumR / ClusterLength[i];
G = sumG / ClusterLength[i];
B = sumB / ClusterLength[i];
}
if ( means[i].r == R && means[i].g == G && means[i].b == B)
iFlag --;//若均值不变则终止循环
else {
means[i].r = R;
means[i].g = G;
means[i].b = B;
}
}
} //迭代结束后为每簇上色表达聚类结果
for (int i = ; i < K; i++) {
for (int j = ; j < ClusterLength[i]; j++) {
Img[Cluster[i][j].height][Cluster[i][j].width].r = means[i].r;
Img[Cluster[i][j].height][Cluster[i][j].width].b = means[i].b;
Img[Cluster[i][j].height][Cluster[i][j].width].g = means[i].g; }
} //释放内存
for (int i = ; i < K; i++) {
free(Cluster[i]);
}
free(Cluster);
free (means);
free(ClusterLength); return ;
} float distance(RGB x, RGB mean)
{
return sqrt( pow((float)(x.b - mean.b),) +
pow((float)(x.g - mean.g),) +
pow((float)(x.r - mean.r),)
);
}
By ascii0x03, 2015.10.19
【C】用C语言提取bmp图片像素,并进行K-means聚类分析——容易遇到的问题的更多相关文章
- 提取bmp图片的颜色信息,可直接framebuffer显示(c版本与python版本)
稍微了解了下linux的framebuffer,这是一种很简单的显示接口,直接写入像素信息即可 配置好的内核,会有/dev/fbn 的接口,于是想能否提前生成一个文件,比如logo.fb,里面仅包含像 ...
- linu下C语言之BMP图片操作编程(上)
BMP文件格式,也被称为位图图像文件或与设备无关的位图文件格式(DIB)或者只是一个位图,是 一个光栅图形 图像文件格式使用 来存储位图,数字,图片,独立的显示设备. 微软已经定义了一个特定的表示颜色 ...
- C语言实现BMP图片生成
## #include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned cha ...
- linu下C语言之BMP图片操作编程(下)
前面提高了一个将BMP左转的程序,右转其实也是类似的操作,就不写了,这节,我们来实现,将一张BMP图进行灰度处理,代码贴上: #include <stdio.h> #include < ...
- linu下C语言之BMP图片操作编程(中)
http://blog.csdn.net/morixinguan/article/details/50719472 关于BMP图的介绍之前已经说过了,最近要用到,又要重新开始学习. 现在实现一个让bm ...
- c语言数字图像处理(一):bmp图片格式及灰度图片转换
本篇文章首先介绍了bmp图片格式,主要参考wiki上的内容,包括bmp文件的存储方式,对于一些常见的bmp文件格式都给了例子,并且对8位 16位RGB555 16位RGB565格式的bmp文件进行了简 ...
- 纯C++代码实现将像素矩阵保存为bmp图片
由于工作需要,时常需要将像素矩阵保存图片显示观看.为此,特地总结了三种使用纯C++代码生成bmp图片的方法.分别是使用自定义数据.从外界导入的txt和csv以及从图片中导入的数据. 1.使用自定义数据 ...
- 用Delphi直接获取bmp图片的像素
用Delphi直接获取bmp图片的像素,并存储显示出.(此像素主要用在LED上显示).希望高手能给出代码啊!! function getImagePixels(f: string): Integer; ...
- bmp图片数据提取
仿照别人的程序写的bmp数据提取C代码,并将提取的数据放到txt文档中 /* date : 2014/06/24 designer :pengxiaoen version : dev4.9.9.0 f ...
随机推荐
- 教你如何利用php.exe运行php文件
教你如何利用php.exe运行php文件 一.总结 一句话总结:就是使用的php.exe,和java中的javac一样,都是有exe,然后有了对应命令,比如php.exe,然后就可以用php命令. 1 ...
- 轻松学习JavaScript十八:DOM编程学习之DOM简单介绍
一DOM概述 DOM(文档对象模型)是HTML和XML的应用程序接口(API).DOM将把整个页面规划成由节点层级构成的文档. DOM描绘了一个层次化的节点树,执行开发者加入,移除和改动页面的某一部分 ...
- 数组replaceObjectAtIndex
执行replaceObjectAtIndex方法前提是_temp_Array必须是可变数组 [_temp_Array replaceObjectAtIndex:2 withObject:tempDat ...
- experiment : 在私有堆和默认进程堆中, 测试能分配的堆空间总和, 每次能分配的最大堆空间
实验环境: Win7X64Sp1 + vs2008, 物理内存16GB. 实验结论: * 进程堆的最大Size并没有使用完剩余的物理内存 * 每次能分配的最大堆空间接近2M, 不管是私有堆 ...
- VS2008智能提示解决办法
最近在VS2008的代码编辑环境中,发现定义了一个类后,然后用类对象点不出对象的属性和方法,于是在网上参考一些资料. 具体步骤如下: 一.开始->Microsoft Visual Studio ...
- js限制文本框input只能输入数字
JS判断只能是数字和小数点. ,文本框只能输入数字代码(小数点也不能输入) 复制代码 代码示例:<input onkeyup="this.value=this.value.replac ...
- UnitOfWork
数据访问层之UnitOfWork 接上文 项目架构开发:数据访问层之IQuery 本章我们继续IUnitOfWork的开发,从之前的IRepository接口中就可以看出,我们并没有处理单元事务, ...
- MapReduce 编程 系列七 MapReduce程序日志查看
首先,假设须要打印日志,不须要用log4j这些东西,直接用System.out.println就可以,这些输出到stdout的日志信息能够在jobtracker网站终于找到. 其次,假设在main函数 ...
- 设置vista和win7进入Debug模式
转载请标明是引用于 http://blog.csdn.net/chenyujing1234 欢迎大家拍砖 设置vista和win7进入Debug模式: 1. bcdedit /copy {curre ...
- latex 常用环境(environment)
align \begin{align} \overline{A \cup B} &= \overline{A} \cap \overline{B}, \\ \overline{A \cap B ...