图像处理与图像分析 您所在的位置:网站首页 灰度图怎么修改形状呢视频 图像处理与图像分析

图像处理与图像分析

2024-07-15 17:01| 来源: 网络整理| 查看: 265

根据输入的灰度图像,分别计算图像的均值、方差等统计特征,并计算图像的直方图特征并以图形方式显示图像的直方图(用C或C++语言实现)。

学习将会依据教材图像处理与图像分析基础(C/C++)版内容展开

在上个笔记中,我们成功对bmp图像进行了读取,那么这次我们将会尝试计算图像的均值、方差等统计特征,并计算图像的直方图特征并以图形方式显示图像的直方图

首先分析一下我们需要的功能:

能够读取图片分析图片每一个像素的灰度值把每一个像素的灰度值存入灰度数组用直方图的形式进行展示计算图像的均值,方差

那么上笔记的代码已经可以实现:图片的读取,分析灰度值,那么我们只需要再加上后面三个功能即可

首先我们还是先来分析书上的代码

算法【2-2】统计灰度直方图

void GetHistogram(BYTE* pGryImg, int width, int height, int* histogram) { BYTE * pCur, * pEnd = pGryImg + width * height; //step.1 初始化 //for(int g = 0;g < 256;g++) histogram[g] = 0; memset(histogram, 0, sizeof(int) * 256); //step.2 直方图统计 for (pCur = pGryImg; pCur histogram[*(pCur++)]++; // 获取当前像素的灰度值并递增对应直方图计数 } // Step 3: 函数结束 return; }

不是很难,可以试一下,首先还是写成C语言

void GetHistogram(uint8_t* pImg, int width, int height, int* histogram) { uint8_t* pCur; uint8_t* pEnd = pImg + width * height; // 初始化直方图数组 memset(histogram, 0, sizeof(int) * 256); // 直方图统计 for (pCur = pImg; pCur uint8_t* pCur; uint8_t* pEnd = pImg + width * height; // 初始化直方图数组 memset(histogram, 0, sizeof(int) * 256); // 直方图统计 for (pCur = pImg; pCur int histogram[256]; //建立一个数组 const char* filename = "E:/code/IDP-Learning-Journey/images/ME.bmp"; FILE* file = fopen(filename, "rb"); uint8_t bmpHeader[54]; size_t bytesRead = fread(bmpHeader, 1, 54, file); int width = *(int*)&bmpHeader[18]; // 宽度信息位于偏移量为 18 的位置 int height = *(int*)&bmpHeader[22]; // 高度信息位于偏移量为 22 的位置 uint8_t* imageData = (uint8_t*)malloc(width * height); fseek(file, 54, SEEK_SET); // 跳过 BMP 文件头 bytesRead = fread(imageData, 1, width * height, file); GetHistogram(imageData, width, height,histogram); for (int i = 0; i SDL_Init(SDL_INIT_VIDEO); // 创建窗口 SDL_Window* window = SDL_CreateWindow("SDL Example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, 0); // 创建渲染器 SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); // 设置绘制颜色为红色 SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); // 清空屏幕 SDL_RenderClear(renderer); // 创建一个矩形 SDL_Rect rect = { 100, 100, 200, 150 }; // 填充矩形 SDL_RenderFillRect(renderer, &rect); // 更新屏幕 SDL_RenderPresent(renderer); // 等待2秒 SDL_Delay(2000); // 清理资源 SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }

这样就配置成功了

SDL是如何绘图的

这个问题很重要,如果不搞清楚我们将无法绘制灰度值直方图

查了一下,分为以下几步

初始化sdl

这一步是为了指定要使用的SDL子系统,比如视频子系统、音频子系统、定时器子系统等。可以选择仅初始化所需的子系统,以提高性能和减少资源占用。*

SDL_Init(SDL_INIT_VIDEO);//我们使用的画图窗口用的是视频子系统

创建窗口

创建一个来展示我们直方图数据的窗口

SDL_Window* window = SDL_CreateWindow("Histogram", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 512, 512, SDL_WINDOW_SHOWN);//窗口名称为直方图,接下来两个参数是窗口在屏幕上的位置,分别在水平和垂直都居中显示,窗口的宽度和高度目前按时512和512,这个后面再调整,再后面一个参数的意思是创建窗口立即显示

创建渲染器

这一步不是很能理解,在我的理解里应该是类似于创建一个画板,可以在窗口里作图的功能

SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);//window为要在其上创建渲染器的窗口指针,-1:指定要使用的渲染器索引。通常将其设置为-1,以便SDL自动选择可用的索引。SDL_RENDERER_ACCELERATED:指定渲染器使用硬件加速,以提高性能。

设置背景颜色

SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);//设置绘制颜色设置为黑色(RGB值为0, 0, 0,表示红、绿、蓝通道均为0,即黑色),同时将透明度设置为255(不透明) SDL_RenderClear(renderer);//然后通过SDL_RenderClear函数清空渲染器,实际上是在规定背景颜色。

在使用SDL进行图形绘制时,首先通常会设置绘制颜色,然后使用SDL_RenderClear函数填充整个渲染目标(通常是窗口)的背景色。

绘制直方图

我尝试了使用for循环每次改变x坐标,y坐标为数组中的值,但是好像失败了

image-20240310130657225

先看代码

int barWidth = 2;// 设置每个直方条的宽度 for (int i = 0; i barX, barY, barWidth, (int)(histogram[i]) }; SDL_RenderFillRect(renderer, &bar); }// 绘制 SDL_RenderPresent(renderer); // 更新renderer

难道是因为,直方图太小了直接看不到吗,如果限定一个高度区间会不会好一点

我思路是这样的

找到灰度值的最大值用规定的最大值去比上灰度值的最大值得到一个比例这样所有的灰度值都乘上这个比例,就可以等比例缩放了

ok,用for循环找最大值和最小值

int maxBarHeight = 0; int minBarHeight = 0; for (int i = 0; i maxBarHeight = histogram[i]; } } for (int i = 0; i minBarHeight = histogram[i]; } }

这样子就有最大值和最小值了,我们的最大值设定为400,因为窗口为512,

宽度还为2,刚好填满窗口

int barWidth = 2; double k = (double)(400) / maxBarHeight;//设比值为k,定义双精度浮点数400,防止整数相除默认算整数 for (int i = 0; i barX, barY, barWidth, (int)(histogram[i] * k) }; //传递参数为,直方图左上角点的坐标,直方图的宽度和高度 SDL_RenderFillRect(renderer, &bar); }

看一下效果

image-20240310133228536

对比一下Photoshop的直方图

image-20240310133811221

再调整一下,把窗口改为512X256,高度改为两百

image-20240310134311229

但是只能展示一瞬间,然后就会结束程序,查过资料之后发现可以添加以下代码来延长展示时间

SDL_Event event; int quit = 0; while (!quit) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = 1; } } }

再次测试可以正常展示,完成目标。

源代码: #define _CRT_SECURE_NO_WARNINGS 1 #include #include #include #include void displayHistogram(const int* histogram, int size) { SDL_Init(SDL_INIT_VIDEO); SDL_Window* window = SDL_CreateWindow("Histogram", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 512, 256, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (renderer == NULL) { printf("Failed to create renderer: %s\n", SDL_GetError()); return; } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); int maxBarHeight = 0; int minBarHeight = 0; for (int i = 0; i maxBarHeight = histogram[i]; } } for (int i = 0; i minBarHeight = histogram[i]; } } int y = 250; int barWidth = 2; double k = (double)(200) / maxBarHeight; for (int i = 0; i barX, barY, barWidth, (int)(histogram[i] * k) }; SDL_RenderFillRect(renderer, &bar); } SDL_RenderPresent(renderer); SDL_Event event; int quit = 0; while (!quit) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = 1; } } } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); } void GetHistogram(uint8_t* pImg, int width, int height, int* histogram) { uint8_t* pCur; uint8_t* pEnd = pImg + width * height; // 初始化直方图数组 memset(histogram, 0, sizeof(int) * 256); // 直方图统计 for (pCur = pImg; pCur int histogram[256]; //建立一个数组 const char* filename = "E:/code/IDP-Learning-Journey/images/ME.bmp"; FILE* file = fopen(filename, "rb"); uint8_t bmpHeader[54]; size_t bytesRead = fread(bmpHeader, 1, 54, file); int width = *(int*)&bmpHeader[18]; // 宽度信息位于偏移量为 18 的位置 int height = *(int*)&bmpHeader[22]; // 高度信息位于偏移量为 22 的位置 uint8_t* imageData = (uint8_t*)malloc(width * height); fseek(file, 54, SEEK_SET); // 跳过 BMP 文件头 bytesRead = fread(imageData, 1, width * height, file); GetHistogram(imageData, width, height,histogram); displayHistogram(histogram, 256); fclose(file); free(imageData); return 0; }

好的,我们目前还需要展示图像的均值和方差以及各部分信息,首先是均值

均值

没有想到特别好的办法,只能想到所有的灰度值相加,除以总的像素数

用C语言写一个函数,传入值为数组元素和宽度以及高度像素信息

double mean(const int* histogram, int width, int height) { size_t sum = 0; size_t totalPixels = width * height; for (size_t i = 0; i double variance = 0.0; for (size_t i = 0; i printf("%d\n", i);//检查发现循环次数无法达到1000000次以上 variance += pow(histogram[i] - mean, 2); } 原因是因为width * height数值太大,需要强制类型转换 不是上面的原因,因为灰度值图像中并没有这么多内存格子,我理解错了

这才是正确的,我真的是看了半个小时,取的数据不是图片所有的数据,而是直方图数据

image-20240310181201358

double StandardDeviation(const int* histogram, int width, int height, double mean) { double variance = 0.0; int totalPixels = width * height; for (int i = 0; i int size = width * height; int cumulativeSum = 0; int middleElement = (size + 1) / 2; for (int i = 0; i return i; } } return 0; }

真的是最顺利的一次

image-20240310182856703

明度和对比度

这个我们一起来看书上【程序 2-5】

void GetBrightContrast(int * histogram,double * bright,double * contrast) { int g; double sum,num;//书上说图像很亮时,int有可能会溢出,所以我这里直接用double double fsum; //step.1 求亮度 for(sum = num = 0, g = 0; g fsum += histogram[g]*(g - *bright) * (g - *bright); } * contrast = sqrt(fsum/(num - 1));//即Std Dev //step.3 结束 return; }

好的观察后发现,明度就是均值,对比度就是标准差

我前面相当于白头脑风暴了

很容易理解我就不再注释了,看一下运行结果

image-20240310184356037

前面还是有点小问题,咱们先用后面的代码,那这样,我们就已经成功得到了图片的亮度,对比度,以及中间值,那么怎么写进直方图里呢

好的,我也没找到好的办法,最好的办法是用SDL的文字库,继续网上加

但是我在测试代码的时候一直提示没有这个库,后来我就想到了能不能直接加在标题上

于是我就创建了一串字符串用作标题,上面传入了计算的明度,对比度以及中间值的数据

代码如下

char windowTitle[100]; sprintf(windowTitle, "Histogram Brightness: %.2f Contrast: %.2f Median: %.2f", a, b, c); SDL_Window* window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 512, 256, SDL_WINDOW_SHOWN);

详细的讲解在上面讲过,这里就不过多赘述了

我知道这个方法可能的确有点投机取巧了,后面也会去寻找解决办法,我觉得Qt虽然能解决,但是还是更想用C语言去自己敲一遍,这个找到解决办法了再来更新笔记

结果

不过我们看一下运行结果

image-20240310191200504

还是很不错的,对比一下Photoshop

image-20240310191246210

是很接近的,可能偏差的原因就是ps用的是rgb,我只用了单通道的灰度值

总体来看不是很难,但是细节很多,需要对C语言有深刻的了解,接下来我把源码放在下面

#define _CRT_SECURE_NO_WARNINGS 1 #include #include #include #include void displayHistogram(const int* histogram, int size,double a,double b,double c) { SDL_Init(SDL_INIT_VIDEO); char windowTitle[100]; sprintf(windowTitle, "Histogram Brightness: %.2f Contrast: %.2f Median: %.2f", a, b, c); SDL_Window* window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 512, 256, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); if (renderer == NULL) { printf("Failed to create renderer: %s\n", SDL_GetError()); return; } SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); int maxBarHeight = 0; int minBarHeight = 0; for (int i = 0; i maxBarHeight = histogram[i]; } } for (int i = 0; i minBarHeight = histogram[i]; } } int y = 250; int barWidth = 2; double k = (double)(200) / maxBarHeight; for (int i = 0; i barX, barY, barWidth, (int)(histogram[i] * k) }; SDL_RenderFillRect(renderer, &bar); } SDL_RenderPresent(renderer); SDL_Event event; int quit = 0; while (!quit) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = 1; } } } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); } void GetHistogram(uint8_t* pImg, int width, int height, int* histogram) { uint8_t* pCur; uint8_t* pEnd = pImg + width * height; // 初始化直方图数组 memset(histogram, 0, sizeof(int) * 256); // 直方图统计 for (pCur = pImg; pCur size_t sum = 0; size_t totalPixels = width * height; for (size_t i = 0; i double variance = 0.0; int totalPixels = width * height; for (int i = 0; i int size = width * height; int cumulativeSum = 0; int middleElement = (size + 1) / 2; for (int i = 0; i return i; } } return 0; } void GetBrightContrast(int * histogram,double * bright,double * contrast) { int g; double sum,num;//书上说图像很亮时,int有可能会溢出,所以我这里直接用double double fsum; //step.1 求亮度 for(sum = num = 0, g = 0; g fsum += histogram[g]*(g - *bright) * (g - *bright); } * contrast = sqrt(fsum/(num - 1));//即Std Dev //step.3 结束 return; } int main() { int histogram[256]; //建立一个数组 const char* filename = "E:/code/IDP-Learning-Journey/images/ME.bmp"; FILE* file = fopen(filename, "rb"); uint8_t bmpHeader[54]; size_t bytesRead = fread(bmpHeader, 1, 54, file); int width = *(int*)&bmpHeader[18]; // 宽度信息位于偏移量为 18 的位置 int height = *(int*)&bmpHeader[22]; // 高度信息位于偏移量为 22 的位置 uint8_t* imageData = (uint8_t*)malloc(width * height); fseek(file, 54, SEEK_SET); // 跳过 BMP 文件头 bytesRead = fread(imageData, 1, width * height, file); GetHistogram(imageData, width, height,histogram); double meanValue = 0; double StandardDeviationValue = 0; double median = 0; double bright, contrast; GetBrightContrast(histogram, &bright, &contrast); median = Median(histogram, width, height); //printf("中间值(Median):%f\n", median); //printf("亮度(Brightness): %lf\n", bright); //printf("对比度(Contrast): %lf\n", contrast); displayHistogram(histogram, 256, bright, contrast,median); fclose(file); free(imageData); return 0; } 感谢您的阅读!


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有