图像处理像素为4的倍数

2018-11-27 08:59:12 u012372584 阅读数 963
  • CSS简介

    通俗易懂的方式快速掌握CSS技术 手把手教你掌握每一个知识点 适合初学者的教程

    563人学习 汤小洋
    免费试看

位图操作函数WIDTHBYTES

一、定义

#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4);

计算图像每行象素所占的字节数目,设置成4的整数倍

//=(((bits) + 31) / 8)

//=((( biWidth* biBitCount) + 31) / 8)

 

二、参数说明:

bits:一个btmap中一行像素的位数总和。

bits:=biWidth* biBitCount;

biWidth为图像的宽即一行 以像素为单位

biBitCount为每个像素所占的位数(bit),也就是一个像素占biBitCount/8个字节;新的BMP 格式支持32 位色。

当biBitCount=1时,8个像素占1个字节;其值必须为1(黑白图像)、

当biBitCount=4时,2个像素占1个字节;4(16 色图)、

当biBitCount=8时,1个像素占1个字节;8(256 色)、

当biBitCount=24时,1个像素占3个字节;24(真彩色图),

 

三、实例:

对于2色图,如果图象宽是31,则每一行需要31位存储,合3个字节加7位,因为字节数必须是4的整倍数,所以应该是4,而此时的 biWidth=31, biBitCount=1, WIDTHBYTES(31*1)=4,和我们设想的一样。

举一个256色的例子,如果图象宽是31,则每一行需要31个字节存储,因为字节数必须是4的整倍数,所以应该是32,而此时的biWidth=31, biBitCount=8, WIDTHBYTES(31*8)=32。

四、相关知识说明

1.将图像文件宽度设置成4的整数倍,只有bmp格式要求采用,其他格式的图像并不要求是4的倍数。

2.bmp格式采用这一做法,我想是为了考虑访问的速度。从文件读出图像数据时,一次读4个字节比一次读1个字节快。但如果图像文件不是4的倍数时,每行最后的像素就不可能[注]采用一次读4个字节(否则就会把下一行的像素也读出)。当然,你仍然可以读出的4字节来,但必须处理:将其中几个字节放在本行,而其余的显示到下一行. 但显然这很不方便,要影响速度。

3. 位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是左到右,扫描行之间是从下到上。Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充

五、另一种计算方式

//=(bmpWidth * biBitCount/8+3)/4*4;

2017-12-09 16:03:14 lingmengxiaotong 阅读数 2978
  • CSS简介

    通俗易懂的方式快速掌握CSS技术 手把手教你掌握每一个知识点 适合初学者的教程

    563人学习 汤小洋
    免费试看

读取图像像素,并保存到二维数组中。图像高度和宽度不是4的倍数


IplImage* src= cvLoadImage("E:\\tupian\\289011.jpg", 0); //导入图  \289011.jpg

int width = src->width;//图片宽度
int height = src->height;//图片高度

uchar *data = (uchar *)src->imageData;

int step = src->widthStep / sizeof(uchar);
uchar *tmp;
for (size_t row = 0; row < height; row++)
{
//uchar* ptr = (uchar*)src->imageData + row*src->width;//获得灰度值数据指针
for (size_t cols = 0; cols < width; cols++)
{
int intensity = data[row*step+cols];
//int intensity = ptr[cols];
v.push_back(intensity);


}
}
count = 0;
for (int i = 0; i < height; ++i)  //修改于7_12
{
for (int j = 0; j < width; ++j)
{
m_Graylevel[i][j] = v[count++];




}




}
2018-11-29 14:25:41 weixin_39437164 阅读数 1506
  • CSS简介

    通俗易懂的方式快速掌握CSS技术 手把手教你掌握每一个知识点 适合初学者的教程

    563人学习 汤小洋
    免费试看

直入主题

直接说结论吧:在使用Opencv时,内部规定,图像每行所占字节数必须是4的倍数,才能保证无误的转换;否则存在异常。

问题描述

题主项目中的图像存在些许像素的裁剪,一开始裁剪列数设置10没有任何问题,当设置其他时候,便存在异常。
于是,题主展开搜寻式查找,这个过程不必描述了,流程存在多个变量,所以按个定位到异常位置。
最终定位到一个这样的函数:
void Forground(uchar CurImg, uchar ForImg)

实现部分暂时不分享了,俩参数分别为入参出参;在实际调用时候,前一个用Mat转来的iplimge * 格式,后一个是用 iplimge * 格式的直接调用输出。

奇怪的事情发生了:

在函数内部直接把出参转为mat时,图像正常显示;
在调用完函数显示图像时,图像异常。

输出结果显示====

内部调用,正常如下图:
在这里插入图片描述

异常如下图:
在这里插入图片描述

同样一张图像,理论上输出一致的,可是现实的的确确这样的输出。
后来陆续修改那个裁剪列数,发现奇数存在异常;裁剪模块是裁剪那个数字的2倍。
百思不得其解,项目工程图像数据量大,自己把那个输出char*类型数据转Mat真的很费时;大概需要0.025秒~

问题原因

继续查找问题,偶然间看到这样一段话:

在这里插入图片描述

简直就是顿悟!
计算机比较亲近的数据都是2的倍数4的倍数,也许Opencv在操作时候,可能存在类似线程块加速的方式,所以做了这个强行规定。
既然使用Opencv,咱也必须守着别人的规则。

题主图像异常原因确实是因为宽度问题(单通道正好一个像素一个字节),在图像宽度奇数时候,他会自动的补成偶数,用哪里的数据补呢?
最简单的,就是近邻像素,那么第二行第一个像素,是实际数据的第二个,类推~
所以这样一直错位。

解决方案

既然知道问题所在,解决方案也就一目了然:

  1. 规避,强行规定裁剪后图像列数是4的倍数;
  2. 补零操作,裁剪后意识到数据不达标时候,末位补零满足4的倍数的要求;
  3. 截断,末位截取几个像素,使之满足4的倍数的要求。
2015-10-17 12:53:18 zhiiyang3344 阅读数 5437
  • CSS简介

    通俗易懂的方式快速掌握CSS技术 手把手教你掌握每一个知识点 适合初学者的教程

    563人学习 汤小洋
    免费试看

无意中在百度文库看到的,哎都是复制粘贴,md那么多错误你还粘贴不是误导别人么。

先转别人的:

位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:   &nbsp; 
      当biBitCount(每个像素所需的位数)=1时,8个像素占1个字节; 
      当biBitCount=4时,2个像素占1个字节; 
      当biBitCount=8时,1个像素占1个字节; 
      当biBitCount=24时,1个像素占3个字节; 
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充。 
一个扫描行所占的字节数计算方法:   DataSizePerLine=   (biWidth*   biBitCount+31)/8;   
我们后面定义了一个宏   #define   WIDTHBYTES(i)&nbsp;&nbsp;&nbsp;   ((i+31)/32*4)   
每一行的字节数必须是4的整倍数,只要调用   
                  WIDTHBYTES(bi.biWidth*bi.biBitCount) 
就能完成这一换算。 
        举一个例子,对于2色图,如果图象宽是31,则每一行需要31位存储,合3个字节加7位,因为字节数必须是4的整倍数,所以应该是4,而此时的              biWidth=31,biBitCount=1,WIDTHBYTES(31*1)=4,和我们设想的一样。再举一个256色的例子,如果图象宽是31,则每一行需要31个字节存储,因为字节数必须是4的整倍数,所以应该是32,而此时的      biWidth=31,biBitCount=8,WIDTHBYTES(31*8)=32, 
和我们设想的一样。


这里我解释一下,Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充”因为我们便是整数大小用的是int(long也是),int占用4个字节,这是32位系统定死的,所以必须是4的整数倍,也就是四个字节造成的!

一个扫描行所占的字节数计算方法:   DataSizePerLine=   (biWidth*   biBitCount+31)/8;   
我们后面定义了一个宏   #define   WIDTHBYTES(i)&nbsp;&nbsp;&nbsp;   ((i+31)/32*4)   
每一行的字节数必须是4的整倍数,只要调用   
                  WIDTHBYTES(bi.biWidth*bi.biBitCount) 
就能完成这一换算。 
        举一个例子,对于2色图(也就是黑白图,也就是1位图biBitCount=1,鄙视百度文库某人说成8位图的错误),如果图象宽是31(也就是31个像素),则每一行需要31位存储,合3个字节加7位,因为字节数必须是4的整倍数,所以应该是4个字节!
而此时的biWidth=31个像素,biBitCount=1位,WIDTHBYTES(31*1)=补齐到4个字节,和我们设想的一样。

再举一个256色图(就是2^8=256种颜色,所以是8位图,一个像素占用一个字节)的例子,如果图象宽是31,则每一行需要31个字节存储,因为字节数必须是4的整倍数,所以应该是32字节,而此时的      biWidth=31像素,biBitCount=8位,WIDTHBYTES(31*8)=32字节, 

和我们设想的一样。


如有不正确的地方请指教,起码我是这么理解的!


2019-12-25 10:06:22 Trent1985 阅读数 1581
  • CSS简介

    通俗易懂的方式快速掌握CSS技术 手把手教你掌握每一个知识点 适合初学者的教程

    563人学习 汤小洋
    免费试看

前言

        本专栏面向所有希望或有兴趣从事数字图像处理工作、学习或研究的朋友,不懂MATLAB和OPENCV没关系,仅需要基础的C语言知识,便可以通过本专栏内容轻松入门数字图像处理方向。目前市面上的数字图像处理书籍种类繁多,往往令人眼花缭乱,不知从何而起,复杂的第三方库调用,也导致了大多数初学者苦不堪言,而本专栏内容将从繁就简,另辟蹊径,以简约明了的逻辑,无任何第三方库依赖的C语言代码,来帮助大家快速掌握,轻松入门, 这也是本专栏和作者的初衷。同时,本专栏内容的逻辑方法,并不依赖于C语言,大家也可以用同样的逻辑方法去学习其他语言的图像处理,这就是掌握学习方法的重要性。

图像像素格式

        对于初学者,往往搞不清楚,一个像素究竟是什么?针对数字图像中的位图而言,一张宽度W,高度H的图像是由W×H个像素点来表示的,每个像素都包含了各自的颜色信息,所以我们的感官才会感知到不同图像各自是什么颜色的。要有颜色的概念,我们就要先了解色彩的深度。

        色彩深度就是色彩的位数,代表了一个像素用多少个二进制位来表示颜色信息。常用的色彩深度有1位(也就是单色),2位(也就是4色CGA),4位(也就是16色VGA),8位(也就是256色),16位(增强色)以及24位和32位真彩色等。听起来对于初学者好像不容易理解,我们这里以黑白二值图、灰度图和24/32位彩色图四类来做说明。

        黑白二值单色图像:图像中每个像素点非黑即白,对于像素值非0即1,每一个像素用一个数值也就是1个二进制位即可表示(一个二进制位代表0或者1),因此,这种黑白二值图也可以叫作单色图,黑白二值图像举例如下图Fig.1所示。

                                                                                 Fig.1黑白二值图像示例

        在Fig.1中,对于任意像素P0,如果它是黑色像素,那么P0=0,反之,P0=1,这就是黑白二值图像中像素P0的数字表示。由于每个像素的数值都在0-255之间,因此,通常我们使用unsigned char类型的数组来存出每个像素的数值。对于Fig.1这张宽高为256×256大小的黑白二值图而言,我们可以用如下数组形式来存储数据:

unsigned char img[256*256]={1,1,1,....};

        8位灰度图像:8位灰度图像是指用8个bit位来表示颜色信息的图像,颜色信息范围位0-255,0是黑色,255是白色,对应的二进制位表示如下:

        0的二进制位表示:00000000

        255的二进制位表示:11111111

        8位灰度图像举例如图Fig.2所示,看起来是一张灰色的图像,但是人物细节等颜色信息明显要比单色二值图像要多很多,因为二值图像只有0和1两个颜色信息,而灰度图有0-255共256个颜色信息;

                                                                                      Fig.2 8位灰度图示例

        在Fig.2中,对于任意像素P0,如果它是黑色像素,那么P0=0,白色P0=255,其他颜色则P0在0到255之间。这就是8位灰度图像中像素P0的数字表示。由于每个像素的数值都在0-255之间,因此,通常我们依旧使用unsigned char类型的数组来存出每个像素的数值。对于Fig.2这张宽高为256×256大小的灰度图而言,我们可以用如下数组形式来存储数据:

unsigned char imggray[256*256]={255,255,255,....};

        24位彩色图像:为了表示更加丰富的彩色信息,我们基于三原色RGB,将每个像素分为了R、G和B三个颜色分量,即红色分量Red,绿色分量Green和蓝色分量Blue。同时,我们对于每个分量都使用8个二进制位也就是1个字节大小来表示它的颜色信息,对应数值范围为0-255。这样,一个像素占用3个字节,24个Bit位,也就是24位彩色图像。颜色信息则是RGB三个颜色分量的组合,由于每个分量可以表示0-255共256种颜色,因此,24位彩色图像像素共有256×256×256种颜色信息,我们也将RGB三个颜色分量叫作三个通道,举例如图Fig.3所示。 

                                                                                  Fig.3 24位彩色图像示例

        在Fig.3中,对于任意像素P0,如果它是黑色像素,那么P0=(R=0,G=0,B=0),白色P0=(R=255,G=255,B=255),通常我们用一个RGB坐标轴的三维坐标来表示,即黑色P0(0,0,0),白色P0(255,255,255)。这就是24位彩色图像中像素P0的数字表示。由于每个像素的RGB数值都在0-255之间,因此,通常我们依旧使用unsigned char类型的数组来存出每个像素的数值。对于Fig.3这张宽高为256×256大小的灰度图而言,由于每个像素有三个通道,我们可以用如下数组形式来存储数据:

unsigned char imgcolor24[256*256*3]={255,255,255,....};

        32位彩色图像:理解了24位彩色图像,那么,32位彩色图像就是在24位彩色图像的基础上添加了一个透明通道alpha位,我们经常看到一些有透明区域的图像,这些透明区域如何控制,就是依靠这个alpha通道来实现的。对于32位彩色图像的每个像素,我们使用RGBA四个颜色分量来表示,A就是透明度分量,同样占用1个字节8个bit,所以,一个像素共占用32个bit,4个字节。我们称32位彩色图像有4个通道,也就是RGBA四通道。对于黑色像素表示为(0,0,0,A),白色像素表示为(255,255,255,A),举例如图Fig.4所示。

                                                                                  Fig.4 32位彩色图像示例

        在Fig.4中,方格子区域就表示这些区域的像素透明通道是0(全透明),我们可以看到的人物区域像素的透明通道是255(不透明)。由于每个像素的RGBA数值都在0-255之间,因此,对于Fig.4这张宽高为256×256大小的灰度图而言,由于每个像素有四个通道,我们可以用如下数组形式来存储数据:

unsigned char imgcolor32[256*256*4]={255,255,255,....};

        对于上述几种格式,是我们比较常见的,而对于初学者,本文将以32位BGRA四通道位图格式为主,来教会大家如何入门数字图像处理。其他几种格式,大家可以简单理解为通道数的差别。

图像读写

        图像读写从专业角度又叫图像编解码,图像编解码是数字图像处理中的重要组成部分,甚至是一个可以单独出书的模块。由于图像格式多种多样,需要对每一种图像进行格式分析,然后单独编解码,同时还要考虑效率和质量问题,因此,也是一个难啃的骨头。对于初学者而言,想要自己实现常用图像的编解码算法,基本不太现实,常用的方法就是调用各种第三方库,比如libjpg/libpng等,或者直接使用opencv/matlab等数字图像处理库。而这些方法对于初学者而言,又是各种配置,各种依赖,苦不堪言。

        对于那些只想学下图像处理算法,并不像涉猎图像编解码,也不想花时间去使用和依赖第三方库的朋友们而言,有没有一种更好的方式,比如以简单的C语言调用来进行图像读写呢?答案是肯定的,这就是github上一份来自MIT的开源代码“stb”。

        stb的代码链接:STB图像编解码

        stb的代码中关于图像读写的部分只有两个头文件:stb_image.h和stb_image_write.h,可以实现常用图像格式如“BMP/JPG/PNG/TGA/HDR/PSD/GIF”等的编解码,而且支持从文件流和文件路径以及内存三个方式进行处理,算法进行了一定的汇编优化,最重要的是代码开源,速度快,效果好,逻辑简单!对于初学者,stb的出现真是一个不小的福音。

        为了更好的从初学者角度考虑,笔者对stb进行了二次封装,以32位bgra四通道格式基础,将stb的几种常用图像格式“BMP/JPG/PNG/TGA”编解码接口进行了合并融合,得到了如下简单的接口:

/***************************ImageFormat**************************/
enum IMAGE_FORMAT{BMP = 0, JPG, PNG, TGA};
/************************************************************
*Function:  Trent_ImgBase_ImageLoad
*Description: Image loading
*Params:    fileName-image file path,eg:"C:\\test.jpg".
*           width-image width.
*           height-image height.
*           component-the bits per pixel.
*                     1           grey
*                     2           grey, alpha
*                     3           red, green, blue
*                     4           red, green, blue, alpha
*Return:    image data.
************************************************************/
unsigned char* Trent_ImgBase_ImageLoad(char* fileName, int* width, int* height, int* component);
/************************************************************
*Function:  Trent_ImgBase_ImageSave
*Description: Image loading
*Params:    fileName-image file path,eg:"C:\\save.jpg".
*           width-image width.
*           height-image height.
*           data-the result image data to save, with format BGRA32.
*           format-image format,0-BMP,1-JPG,2-PNG,3-TGA
*Return:    0-OK.
************************************************************/
int Trent_ImgBase_ImageSave(char const *fileName, int width, int height, const void* data, int format);

        在上述封装代码中,我们可以看到,stb的多个接口被合并为了两个接口,Trent_ImgBase_ImageLoad图像加载和Trent_ImgBase_ImageSave图像保存接口,分别使用图像路径进行操作,简单明了,更加易用。由于stb源代码中本身对于bmp和jpg格式是返回24位三通道图像数据的,为了方便初学者学习,笔者统一将其扩充为了32位bgra格式,完整的封装代码如下:

#include"f_SF_ImgBase_RW.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include<stdlib.h>
#include<string.h>
#include<math.h>

inline unsigned char* f_TImageLoad(char* fileName, int* width, int* height, int* component, int redcomp)
{
        unsigned char* tempData = stbi_load(fileName, width, height, component, redcomp);
        //printf("component:  %d", *component);
        //根据像素通道数component进行判断,分别将8/24/32位转换为32bgra格式数据
        if(*component == 4)
        {
             unsigned char* srcData = (unsigned char*)malloc(sizeof(unsigned char) * *width * *height * 4);
            unsigned char* pSrc = srcData;
            unsigned char* pTemp = tempData;
            for(int j = 0; j < *height; j++)
            {
                for(int i = 0; i < *width; i++)
                {
                     pSrc[0] = pTemp[2];
                     pSrc[1] = pTemp[1];
                     pSrc[2] = pTemp[0];
                     pSrc[3] = pTemp[3];
                     pSrc += 4;
                     pTemp += 4;
                }
            }
            free(tempData);
            return srcData;
        }
        else if(*component == 3)
        {
            unsigned char* srcData = (unsigned char*)malloc(sizeof(unsigned char) * *width * *height * 4);
            unsigned char* pSrc = srcData;
            unsigned char* pTemp = tempData;
            for(int j = 0; j < *height; j++)
            {
                for(int i = 0; i < *width; i++)
                {
                     pSrc[0] = pTemp[2];
                     pSrc[1] = pTemp[1];
                     pSrc[2] = pTemp[0];
                     pSrc[3] = 255;
                     pSrc += 4;
                     pTemp += 3;
                }
            }
            free(tempData);
            *component = 4;
            return srcData;
        } 
        else if(*component == 1)
        {
            unsigned char* srcData = (unsigned char*)malloc(sizeof(unsigned char) * *width * *height * 4);
            unsigned char* pSrc =  (unsigned char*)srcData;
            unsigned char* pTemp = tempData;
            for(int j = 0; j < *height; j++)
            {
                for(int i = 0; i < *width; i++)
                {
                     int gray = *pTemp++;
                     pSrc[0] = gray;
                     pSrc[1] = gray;
                     pSrc[2] = gray;
                     pSrc[3] = 255;
                     pSrc += 4;
                }
            }
            free(tempData);
            *component = 4;
            return srcData;
        }
        else
            return NULL;
};
inline int f_TImageSavePng(char const *fileName, int width, int height, int component, const void  *data, int stride_in_bytes)
{
        unsigned char* pSrc = (unsigned char*)data;
        for(int j = 0; j < height; j++)
        {
             for(int i = 0; i < width; i++)
             {
                 int temp = pSrc[0];
                 pSrc[0] = pSrc[2];
                 pSrc[2] = temp;
                 pSrc+=4;
             }
        }
	return stbi_write_png(fileName, width, height, component, data, stride_in_bytes);
};
inline int f_TImageSaveBmp(char const *fileName, int width, int height, int component, const void  *data)
{
        unsigned char* pSrc =  (unsigned char*)data;
        for(int j = 0; j < height; j++)
        {
             for(int i = 0; i < width; i++)
             {
                 int temp = pSrc[0];
                 pSrc[0] = pSrc[2];
                 pSrc[2] = temp;
                 pSrc+=4;
             }
        }
	return stbi_write_bmp(fileName, width, height, component, data);
};
inline int f_TImageSaveTga(char const *fileName, int width, int height, int component, const void  *data)
{
        unsigned char* pSrc =  (unsigned char*)data;
        for(int j = 0; j < height; j++)
        {
             for(int i = 0; i < width; i++)
             {
                 int temp = pSrc[0];
                 pSrc[0] = pSrc[2];
                 pSrc[2] = temp;
                 pSrc+=4;
             }
        }
	return stbi_write_tga(fileName, width, height, component, data);
};

inline int f_TImageSaveJpg(char const *fileName, int width, int height, int component, const void  *data, int quality)
{
        unsigned char* pSrc = (unsigned char*)data;
        for(int j = 0; j < height; j++)
        {
             for(int i = 0; i < width; i++)
             {
                 int temp = pSrc[0];
                 pSrc[0] = pSrc[2];
                 pSrc[2] = temp;
                 pSrc+=4;
             }
        }
	return stbi_write_jpg(fileName, width, height, component, data, quality);
};
/************************************************************
*Function:  Trent_ImgBase_ImageLoad
*Description: Image loading
*Params:    fileName-image file path,eg:"C:\\test.jpg".
*           width-image width.
*           height-image height.
*           component-the bits per pixel.
*                     1           grey
*                     2           grey, alpha
*                     3           red, green, blue
*                     4           red, green, blue, alpha
*Return:    image data.
************************************************************/
unsigned char* Trent_ImgBase_ImageLoad(char* fileName, int* width, int* height, int* component)
{
	int redcomp = 0;
	return f_TImageLoad(fileName, width, height, component, redcomp);
};
/************************************************************
*Function:  Trent_ImgBase_ImageSave
*Description: Image loading
*Params:    fileName-image file path,eg:"C:\\save.jpg".
*           width-image width.
*           height-image height.
*           data-the result image data to save, with format BGRA32.
*           format-image format,0-BMP,1-JPG,2-PNG,3-TGA
*Return:    0-OK.
************************************************************/
int Trent_ImgBase_ImageSave(char const *fileName, int width, int height, const void* data, int format)
{
	int component = 4;
	int ret = 0;
    //判断图像格式,根据格式进行图像保存
	switch(format)
	{
	case 0://bmp
		ret = f_TImageSaveBmp(fileName, width, height, component, data);
		break;
	case 1://jpg
		ret = f_TImageSaveJpg(fileName, width, height, component, data, 100);
		break;
	case 2://png
		ret = f_TImageSavePng(fileName, width, height, component, data, width * 4);
		break;
	case 3://tga
		ret = f_TImageSaveTga(fileName, width, height, component, data);
		break;
	default:
		printf("Trent_SF_ImgBase_ImageSave ERROR!");
		break;
	}
	return 0;
};

这两个接口的调用代码如下所示:

#include "stdafx.h"
#include"imgRW\f_SF_ImgBase_RW.h"

int _tmain(int argc, _TCHAR* argv[])
{
	//定义输入图像路径
	char* inputImgPath = "C://Test.jpg";
	//定义输出图像路径
	char* outputImgPath = "D://Test_Res.jpg";
	//定义图像宽高信息
	int width = 0, height = 0, component = 0, stride = 0;
	//图像读取(得到32位bgra格式图像数据)
	unsigned char* bgraData = Trent_ImgBase_ImageLoad(inputImgPath, &width, &height, &component);
	stride = width * 4;
	//其他图像处理操作(这里以32位彩色图像灰度化为例)
	//////////////////////////IMAGE PROCESS/////////////////////////////
	unsigned char* pSrc = bgraData;
	for(int j = 0; j < height; j++)
	{
		for(int i = 0; i < width; i++)
		{
			int gray = (pSrc[0] + pSrc[1] + pSrc[2]) / 3;
			pSrc[0] = pSrc[1] = pSrc[2] = gray;
			pSrc += 4;
		}
	}
	////////////////////////////////////////////////////////////////////
	//图像保存
	int ret = Trent_ImgBase_ImageSave(outputImgPath, width, height, bgraData, JPG);
	free(bgraData);
	return 0;
}

        这段测试代码中,我们使用简单的32位彩色图像灰度化效果来进行说明,对应给出测试效果图如下图5所示,简单的几行代码,快速实现了图像读写和32位彩色图像灰度化处理。

                                                                                      Fig.5 图像读写测试

        对于测试代码中,我们使用到了stride,这个概念很多初学者会产生疑惑,不知道是什么,这里给大家简单讲解一下。Stride表示图像数据在内存中的行跨度。这个行跨度并不一定是图像每一行数据的真实宽度。通常在内存中,图像的行数据是以4字节对齐的,也就是行跨度的值是4的倍数。对于32位bgra格式的图像,他的行跨度Stride=width*4,本身就是4的倍数,因此Stride与真实数据的宽度一致,不用考虑对齐问题。而对于24位rgb或bgr格式,它的每一行真实的图像数据是width*3,而这个数字并不一定是4的倍数,比如:

        一行有 11 个像素(Width = 11), 对一个 24 位(每个像素 3 字节)的图像, Stride = 11 * 3 + 3 = 36,而真实的行数据位11*3=33,这是就出现了偏差,而这个偏差值3就是扩展出来用于4字节对齐的部分。

        本文中考虑的是32位图像,大家可以忽略stride,但是,对于其他格式图像,这里我们给出一个Stride的计算公式:

        ①Stride = 每像素占用的字节数(也就是像素位数/8) * Width;

     ②如果 Stride 不是 4 的倍数, 那么 Stride = Stride + (4 - Stride mod 4);

         这里,我们给出整个工程的代码:C语言图像读写代码

        上面内容作为本专栏的第一个章节,我们用较为简单和通俗易懂的方式,来讲解了图像像素和图像读写,可能没有专业书籍那么专业,但是,笔者的宗旨是让每一个初学者能够轻松入门!

        最后,谈一下对于初学者的一些建议:对于学习图像算法,个人觉得,还是不要使用opencv和matlab的好,为什么?无论是opencv还是matlab或者其他类似的库,都只是一种图像处理工具,他们功能强大,封装了各种图像算法,但是,你在使用它的时候,往往是简单的调用它所提供的接口,而不是去了解它的具体算法,长此以往,不利于图像算法的学习。实践出真知,这才是学好算法的王道!

        本人QQ1358009172,有什么疑问欢迎相互讨论!