• 【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语言代码供参考:

    1 #pragma once
    2 
    3 #include "targetver.h"
    4 
    5 #include <stdio.h>
    6 #include <tchar.h>
    7 #include <windows.h>
    8 #include "bitmap.h"
    9 #include <math.h>
    View Code

    bitmap.h:

     1 #pragma pack (1)//字节对齐的控制!非常注意!
     2 /*
     3 typedef struct tagBITMAPFILEHEADER
     4 {
     5     WORD bfType;//位图文件的类型,必须为BM(1-2字节)
     6     DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
     7     WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
     8     WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
     9     DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)
    10     //文件头的偏移量表示,以字节为单位
    11 }BITMAPFILEHEADER;
    12 
    13 typedef struct tagBITMAPINFOHEADER{
    14     DWORD biSize;//本结构所占用字节数(15-18字节)
    15     LONG biWidth;//位图的宽度,以像素为单位(19-22字节)
    16     LONG biHeight;//位图的高度,以像素为单位(23-26字节)
    17     WORD biPlanes;//目标设备的级别,必须为1(27-28字节)
    18     WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节)
    19     //4(16色),8(256色)16(高彩色)或24(真彩色)之一
    20     DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
    21     //1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
    22     DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
    23     LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
    24     LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
    25     DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
    26     DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)
    27 }BITMAPINFOHEADER;
    28 
    29 typedef struct tagRGBQUAD{
    30     BYTE rgbBlue;//蓝色的亮度(值范围为0-255)
    31     BYTE rgbGreen;//绿色的亮度(值范围为0-255)
    32     BYTE rgbRed;//红色的亮度(值范围为0-255)
    33     BYTE rgbReserved;//保留,必须为0
    34 }RGBQUAD;
    35 */
    36 typedef struct
    37 {
    38     BYTE b;
    39     BYTE g;
    40     BYTE r;
    41 }RGB;
    42 
    43 //带有坐标的颜色RGB表示
    44 typedef struct
    45 {
    46     RGB rgb;
    47     int height;
    48     int width;
    49 } RGB_EX;
    50 #pragma pack ()//字节对齐的控制
    View Code

    main.c:

      1 // 针对图片实现K-means聚类算法.cpp : 定义控制台应用程序的入口点。
      2 #include "stdafx.h"
      3 
      4 float distance(RGB x, RGB mean);
      5 int kmeans_img(RGB **Img, LONG ImgWidth, LONG ImgHeight, ULONG lCount, USHORT K);
      6 
      7 int _tmain(int argc, _TCHAR* argv[])
      8 {
      9 //#pragma pack (1)//字节对齐的控制!非常注意!
     10     BITMAPFILEHEADER fileHeader;
     11     BITMAPINFOHEADER infoHeader;
     12     FILE* pfin; fopen_s(&pfin, "test2.bmp","rb");
     13     FILE* pfout; fopen_s(&pfout, "ouput.bmp","wb");
     14 //ReadtheBitmapfileheader;
     15     fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, pfin);
     16 //ReadtheBitmapinfoheader;
     17     fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, pfin);
     18 //为简化代码,只处理24位彩色
     19     if(infoHeader.biBitCount==24) {
     20         int size = infoHeader.biWidth * infoHeader.biHeight;
     21         RGB **ppImg = NULL;
     22         int r;
     23         //开辟空间并读入图片
     24         //RGB img[infoHeader.biHeight][infoHeader.biWidth];  //这里有错误,尺度改为常亮
     25         //fread(ppImg, sizeof(RGB), size, pfin);
     26         ppImg = (RGB**)malloc(infoHeader.biHeight * sizeof(RGB*));
     27         if (!ppImg)
     28             return -1;
     29         //注意!需要处理补齐字节问题:每行字节数目必须是4的整数倍
     30         r = infoHeader.biWidth % 4;
     31         for (int i = 0; i < infoHeader.biHeight; i++) {
     32             ppImg[i] = (RGB*)malloc(sizeof(RGB) * infoHeader.biWidth);
     33             if (ppImg[i]) {
     34                 fread(ppImg[i], sizeof(RGB), infoHeader.biWidth, pfin);
     35                 fseek(pfin, r, SEEK_CUR);
     36             }
     37             else
     38                 return -1;
     39         }
     40         
     41         /*
     42         //把第50行染成黑色
     43         int i=0;
     44         for(;i<infoHeader.biWidth;i++) {
     45             ppImg[50][i].b = ppImg[50][i].g = ppImg[50][i].r = 0;
     46         }
     47         */
     48 
     49         kmeans_img(ppImg, infoHeader.biWidth, infoHeader.biHeight, 2000, 5);
     50 
     51         //将修改后的图片保存到文件
     52         fileHeader.bfSize = infoHeader.biHeight * infoHeader.biWidth * 3 + fileHeader.bfOffBits;
     53         fwrite(&fileHeader,sizeof(fileHeader),1,pfout);
     54         fwrite(&infoHeader,sizeof(infoHeader),1,pfout);
     55         for (int i = 0; i < infoHeader.biHeight; i++) {
     56             fwrite(ppImg[i],sizeof(RGB),infoHeader.biWidth,pfout);
     57             int temp = r;
     58             while (temp--)
     59             {
     60                 fputc(0, pfout);
     61             }
     62         }
     63 
     64         //释放图片占用内存
     65         for (int i = 0; i < infoHeader.biHeight; i++)
     66             free(ppImg[i]);
     67         free(ppImg);
     68     }
     69     fclose(pfin);
     70     fclose(pfout);
     71 //#pragma pack ()
     72     return 0;
     73 }
     74 
     75 /*
     76 对图片像素使用K-means算法聚类,聚成K类
     77 Img:RGB矩阵形式的图片。第一维是高度Height。Img[ImgHeight][ImgWidth]。
     78 为保证算法正确性,图片中应已经剔除了补齐字节用的00
     79 ImgWidth:图片宽
     80 ImgHeight:图片高
     81 lCount:迭代次数
     82 K:聚类数目
     83 
     84 */
     85 int kmeans_img(RGB **Img, LONG ImgWidth, LONG ImgHeight, ULONG lCount, USHORT K)
     86 {
     87     int iFlag;//收敛后置为0
     88     RGB *means = (RGB*)malloc(K * sizeof(RGB));//K个中心
     89     RGB_EX **Cluster = NULL;//存放簇
     90     int *ClusterLength = NULL;
     91     Cluster = (RGB_EX**)malloc(K * sizeof(RGB_EX*));
     92     ClusterLength = (int *)malloc(K * sizeof(int));
     93     for (int i = 0; i < K; i++) {
     94         //随意指定K个中心,应该还有更好的算法.
     95         means[i] = Img[(ImgHeight/(i+1))-1][(ImgWidth/(i+1))-1];
     96         //开辟簇的存储空间
     97         Cluster[i] = (RGB_EX*)malloc(ImgHeight * ImgWidth * sizeof(RGB_EX));
     98     }
     99 
    100     iFlag = K;
    101     //开始迭代
    102     while (lCount-- && iFlag)
    103     {
    104         iFlag = K;
    105         //每次聚类前要初始化
    106         for (int i = 0; i < K; i++)
    107             ClusterLength[i] = 0;
    108 
    109         //对每个像素循环,归置到相应的簇里
    110         for (int i = 0; i < ImgHeight; i++) {
    111             for (int j = 0; j < ImgWidth; j++) {
    112                 int iClusterIndex = 0;
    113                 float fMinDistance = 255 * 255 + 255 *255 + 255 * 255;
    114                 float d = 0;
    115                 for (int k = 0; k < K; k++) {
    116                     d = distance(Img[i][j], means[k]);
    117                     fMinDistance = fMinDistance > d ? iClusterIndex = k, d : fMinDistance;
    118                 }
    119                 Cluster[iClusterIndex][ClusterLength[iClusterIndex]].rgb = Img[i][j];
    120                 Cluster[iClusterIndex][ClusterLength[iClusterIndex]].height = i;
    121                 Cluster[iClusterIndex][ClusterLength[iClusterIndex]++].width = j;
    122             }
    123         }
    124 
    125         //重新计算每个簇的均值
    126         for (int i = 0; i < K; i++) {
    127             unsigned long sumR = 0, sumG = 0, sumB = 0;
    128             BYTE R = 0, G = 0, B = 0;
    129             for (int j = 0; j < ClusterLength[i]; j++) {
    130                 sumR += Cluster[i][j].rgb.r;
    131                 sumG += Cluster[i][j].rgb.g;
    132                 sumB += Cluster[i][j].rgb.b;
    133             }
    134             if (ClusterLength[i]) {
    135                 R = sumR / ClusterLength[i];
    136                 G = sumG / ClusterLength[i];
    137                 B = sumB / ClusterLength[i];
    138             }
    139             if ( means[i].r == R && means[i].g == G && means[i].b == B)
    140                 iFlag --;//若均值不变则终止循环
    141             else {
    142                 means[i].r = R;
    143                 means[i].g = G;
    144                 means[i].b = B;
    145             }
    146         }
    147     }
    148 
    149     //迭代结束后为每簇上色表达聚类结果
    150     for (int i = 0; i < K; i++) {
    151         for (int j = 0; j < ClusterLength[i]; j++) {
    152             Img[Cluster[i][j].height][Cluster[i][j].width].r = means[i].r;
    153             Img[Cluster[i][j].height][Cluster[i][j].width].b = means[i].b;
    154             Img[Cluster[i][j].height][Cluster[i][j].width].g = means[i].g;
    155 
    156         }
    157     }
    158 
    159     //释放内存
    160     for (int i = 0; i < K; i++) {
    161         free(Cluster[i]);
    162     }
    163     free(Cluster);
    164     free (means);
    165     free(ClusterLength);
    166 
    167     return 0;
    168 }
    169 
    170 float distance(RGB x, RGB mean)
    171 {
    172     return sqrt( pow((float)(x.b - mean.b),2) + 
    173                 pow((float)(x.g - mean.g),2) + 
    174                 pow((float)(x.r - mean.r),2) 
    175                 );
    176 }
    View Code

    By ascii0x03, 2015.10.19

  • 相关阅读:
    Ubuntu Linux 开发者笔记本
    jQuery – 随机排列 item
    MemberwiseClone 关于 string 浅拷贝的一个误解
    在Mac OS X中配置Apache + PHP + MySQL
    ASP.NET MVC 3和Razor中的@helper 语法
    textpattern 在 nginx 上的 rewrite 规则
    错误:无法将带[]的索引应用于ConnectionStringsCollection类型的表达式
    F# 3.0新特性简介
    开发中状态到底用数字还是字符串
    将网站中用户上传的零散小文件存储在MongoDB中的.net解决方案
  • 原文地址:https://www.cnblogs.com/ascii0x03/p/4892647.html
Copyright © 2020-2023  润新知