【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 ...
随机推荐
- [Vue] Import component into page
Components are one of the most powerful features of Vue. Let's take a look at how to write our first ...
- Linux开发环境搭建与使用系列教程
00.Linux开发环境搭建与使用1——Linux简史 01.Linux开发环境搭建与使用2——Linux系统(ubuntu)安装方案 02.Linux开发环境搭建与使用3——通过虚拟机安装系统(ub ...
- 如何安全退出多个Activity
我们在项目开发的时候可能会遇到安全退出应用的场景,如何能够安全退出多个Activity?网上有很多方法,如下: 1.抛异常退出 该方法通过抛异常,使程序Force Close.验证可以,但是,需要解决 ...
- Tools:downloading and Building EDK II工具篇:安装/使用EDKII源代码获取/编译工具[2.3]
Tools:Installing and using the Required Tools for downloading and Building EDK II工具篇:安装/使用EDKII源代码获取 ...
- plist文件无法打包进.a静态库中
问题: 之前一直在做静态库的编写与维护,也一直知道静态库的图片资源是没办法打进.a中的.可是突然有个想法.由于有非常多參数的配置是在一个plist文件里的.尽管也知道这是一个plist文件,可是想想和 ...
- UITableView的一些常用设置和代理方法
- (void)viewDidLoad { [super viewDidLoad]; tableview = [[UITableView alloc]initWithFrame:CGRectMake( ...
- python 使用顺序表实现栈和队列
栈: # -*- coding: utf-8 -*- # @author: Tele # @Time : 2019/04/24 下午 2:33 # 采用list(顺序表)实现栈结构,后入先出 clas ...
- java命名规则(转)
1. JAVA源文件的命名 JAVA源文件名必须和源文件中所定义的类的类名相同. 2. Package的命名 Package名的第一部分应是小写ASCII字符,并且是顶级域名之一,通常是com.edu ...
- OpenCV中CvSVM部分函数解读
CvSVM::predict函数解析:无论是Mat接口还是CvMat接口终于都是通过指针的形式调用的.也就是终于都是调用的下面函数实现的 float CvSVM::predict( const flo ...
- centos7环境下mysql5.7的安装与配置(免安装版)
最近无事闲来折腾虚拟机,以前都是折腾云服务器,现在自己捣捣.看到mysql的教程蛮好的,准备做个笔记.原文来自mysql5.7的安装与配置(centos7环境) 第一步:下载mysql ? 1 [ro ...