关于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聚类分析——容易遇到的问题的更多相关文章

  1. 提取bmp图片的颜色信息,可直接framebuffer显示(c版本与python版本)

    稍微了解了下linux的framebuffer,这是一种很简单的显示接口,直接写入像素信息即可 配置好的内核,会有/dev/fbn 的接口,于是想能否提前生成一个文件,比如logo.fb,里面仅包含像 ...

  2. linu下C语言之BMP图片操作编程(上)

    BMP文件格式,也被称为位图图像文件或与设备无关的位图文件格式(DIB)或者只是一个位图,是 一个光栅图形 图像文件格式使用 来存储位图,数字,图片,独立的显示设备. 微软已经定义了一个特定的表示颜色 ...

  3. C语言实现BMP图片生成

    ## #include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned cha ...

  4. linu下C语言之BMP图片操作编程(下)

    前面提高了一个将BMP左转的程序,右转其实也是类似的操作,就不写了,这节,我们来实现,将一张BMP图进行灰度处理,代码贴上: #include <stdio.h> #include < ...

  5. linu下C语言之BMP图片操作编程(中)

    http://blog.csdn.net/morixinguan/article/details/50719472 关于BMP图的介绍之前已经说过了,最近要用到,又要重新开始学习. 现在实现一个让bm ...

  6. c语言数字图像处理(一):bmp图片格式及灰度图片转换

    本篇文章首先介绍了bmp图片格式,主要参考wiki上的内容,包括bmp文件的存储方式,对于一些常见的bmp文件格式都给了例子,并且对8位 16位RGB555 16位RGB565格式的bmp文件进行了简 ...

  7. 纯C++代码实现将像素矩阵保存为bmp图片

    由于工作需要,时常需要将像素矩阵保存图片显示观看.为此,特地总结了三种使用纯C++代码生成bmp图片的方法.分别是使用自定义数据.从外界导入的txt和csv以及从图片中导入的数据. 1.使用自定义数据 ...

  8. 用Delphi直接获取bmp图片的像素

    用Delphi直接获取bmp图片的像素,并存储显示出.(此像素主要用在LED上显示).希望高手能给出代码啊!! function getImagePixels(f: string): Integer; ...

  9. bmp图片数据提取

    仿照别人的程序写的bmp数据提取C代码,并将提取的数据放到txt文档中 /* date : 2014/06/24 designer :pengxiaoen version : dev4.9.9.0 f ...

随机推荐

  1. [React] Test friendly approach

    Add functional function such as change state, this should have tests covered. For example, in a comp ...

  2. [React] Understand React.Children Utilities

    The data contained in this.props.children is not always what you might expect. React provides React. ...

  3. BootstrapTable的使用教程

    官方网站:http://bootstrap-table.wenzhixin.net.cn/参考文档:http://issues.wenzhixin.net.cn/bootstrap-table/ind ...

  4. 汉高澳大利亚matrix矩阵计算器

    我在梦中的超级计算机超级计算机锯,使用大量阵列的cpu记忆,完成并行计算.一个手机制造商由于使用普通机械提供的服务,往往造成停机.是铁道部列车网络售票的事实. 无法使用云服务.上万台计算机并行处理,因 ...

  5. 安装CentOS6.2操作系统

    原创作品,出自 "深蓝的blog" 博客,欢迎转载,转载时请务必注明出处,否则追究版权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong ...

  6. Node.js学习疑惑整理

    1.Node.js 在调用某个包时,会首先检查包中 package.json 文件的 main 字段,将其作为 包的接口模块,如果 package.json 或 main 字段不存在,会尝试寻找 in ...

  7. hadoop中国字、词频统计和排序

    例如需求,下面: 有被看作图输入文件中. 代表ip地址,之后的偶数列代表搜索词.数字(奇数列)代表搜索次数.使用"\t"分隔.如今须要对搜索词进行分词并统计词频,此处不考虑搜索次数 ...

  8. 经典书单 —— 语言/算法/机器学习/深度学习/AI/CV/PGM

    0.0 计算机科学 <Lex 与 Yacc> Think Complexity(使用 Python 语言) GitHub - AllenDowney/ThinkComplexity: Co ...

  9. jQuery在线选座订座(高铁版)

    除了电影院在线选座,我们还会接触到飞机机舱选座,当然也有汽车票火车票选座的.假如有一天买火车票也提供在线选座,那么今天我来给大家介绍下如何使用jQuery选座插件完成高铁列车座位布置.选座.不同等级座 ...

  10. Spring中的Interceptor 拦截器 专题

    spring-webmvc-4.3.14.RELEASE.jar org.springframework.web.servlet.DispatcherServlet#doDispatch /** * ...