C语言实现一、二维数组动态内存分配与释放以及图像的线性插值放大与优化
2016-07-28 14:11
507 查看
1.1前言
1.本文是博主本着虚心学习的态度与大家交流,博文难免会有错误之处,希望大家指正;2.本文是针对对C语言以及图像放大的基础讨论,如是大牛们可以直接忽略本文;
3.运行环境:由于在不同电脑配置和系统上的运行时间有差异,本程序的测试平台:电脑CPU为Intel奔腾双核E6500,主频2.93GHZ内存4GB.虚拟机为10.0版的VMware,linux系统为Fedora16
要讲程序的运行速度,还是要从计算机的底层实现为出发点,以下一个图像的两倍线性放大论证此事实。刚刚接触图像处理,要完成图像的两倍线性放大,一想到图像有长与宽,最先想到用二维数组来实现,每次插值采用时间复杂度为O(n^2)的两个for循环来实现,插几次值就OK了。做完之后,大师说这个程序还有优化的空间,因为计算机的内存时一块一维连续的空间,如果这里出发采用一维数组去实现,效果会更好,插2次值就OK,而且每次插值采用的时间复杂度为O(n)。同时涉及到一维数组及二维数组的在堆的动态内存分配,以及以一位数组、二维数组作为函数的形参,进行参数传递。
主旨: 采用隔行隔列插入,左右及上下取均值把640x480的图像放大2倍,变成1280x960的图像,测试程序运行效率时选择了稍大点的数据,效果会比较明显。
2.1插值函数参数为二维数组
紫色框的圆代表原始数据,首先上下取均值插(√),再左右取均值插(×),再取四个均值插(※),最后插最后一列和最后一行。数据来源于计算机生成的伪随机数,为了方便测试该程序,改用较大分辨率2048x1536(现实没见到这种分辨率,按4:3的比例赋的值,测试用即可)程序从linux平台考过来的,格式可能会有一些乱。
源码如下:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <sys/time.h> typedef unsigned short dbyte;//图像每个像素点为双字节(双char型),且都大于零;char为8bit,short为16bit,用short来表示双char #define H 1536 //表示图像分辨率的高度 #define W 2048 //表示图像分辨率的宽度 int interpolation_expansion(dbyte **original_data, dbyte **processed_data);//插值函数声明 int main(int argc, int *argv[]) { int i, j; dbyte **input_data, **output_data; //内存分配 input_data = (dbyte **)malloc(H*sizeof(dbyte *));//为二维数组H行分配dbyte*内存 for(i = 0; i < H; i++) { input_data[i] = (dbyte *)malloc(W*sizeof(dbyte));//为每行中应放存放多少个dbyte元素分配内存 memset(input_data[i],0,W);//初始化内存 } output_data = (dbyte **)malloc(2*H*sizeof(dbyte *)); for(i = 0; i < 2*H; i++) { output_data[i] = (dbyte *)malloc(2*W*sizeof(dbyte)); memset(output_data[i],0,2*W); } for(i = 0;i < H; i++) { for(j = 0; j < W; j++) { input_data[i][j] = rand();//数据采用伪随机数 } } for(i = 238; i < 241; i++) { for(j = 320; j < 324; j++) printf("[%d][%d]_ %d ",i , j, input_data[i][j]); printf("\n"); } struct timeval tpstart, tpend; float timeuse; gettimeofday(&tpstart, NULL);//获取函数调用之前的时间 interpolation_expansion(input_data, output_data); gettimeofday(&tpend, NULL);//获取函数调用结束的时间 printf("_________________________<_________>_________________________\n"); for(i = 476; i < 481; i++) { for(j = 640; j < 648; j++) printf("[%d][%d]_%d ", i, j, output_data[i][j]); printf("\n"); } //计算函数调用多花费的时间,单位为秒 timeuse = 1000000*(tpend.tv_sec - tpstart.tv_sec)+ tpend.tv_usec - tpstart.tv_usec; timeuse /= 1000000; printf("The usetime TWO-dimensional array is %fS\n",timeuse); //释放内存,【好像不用循环直接free(input_data)也可以,前提是需要知道第二维,我不是很确定】 for(i = 0; i < H; i++) { free(input_data[i]); } for(i = 0; i < 2*H; i++) { free(output_data[i]); } } int interpolation_expansion(dbyte **original_data, dbyte **processed_data)//插值函数的原型 { int i, j; int last_line = 2*H -1; int last_column = 2*W -1; short tmp_data; //set values for expansion array 把原来图片的像素点数据插入到需要放大的图片上(下标[偶数][偶数]标示○) for(i = 0;i < H; i++) { for(j = 0; j < W; j++) { processed_data[2*i][2*j] = original_data[i][j]; } } //insert values into that subscript is odd line caculating by up number and down number用已知值进行上下取均值插入(下标[偶数][奇数]标示√) for(i = 1; i < 2*(H-1); i += 2)//height_line { for(j = 0;j < 2*W; j += 2)//width_column { processed_data[i][j] = (processed_data[i-1][j] + processed_data[i+1][j])/2; } } //insert values into that subscript is odd column caculating by left number and right number左右取均值插入(下标[奇][偶]标示×) for(i = 0; i < 2*H; i += 2)//height_line { for(j = 1;j < 2*(W-1); j += 2)//width_column { processed_data[i][j] = (processed_data[i][j-1] + processed_data[i][j+1])/2; } } //insert values into that both weight and height are odd numbers 对周围四个取均值(下标[奇][奇]标示※) for(i = 1; i < 2*(H-1); i += 2)//height_line { for(j = 1; j < 2*(W-1); j += 2) { processed_data[i][j] = (processed_data[i-1][j-1] + processed_data[i-1][j+1] + processed_data[i+1][j-1] + processed_data[i+1][j+1])/4; } } //insert values into the last line and column对最后一行和最后一列进行插值 for(i = 0; i< 2*H-1; i++)//insert values into last colum { if((tmp_data = 2*processed_data[i][last_column-1]-processed_data[i][last_column-2])>0) { processed_data[i][last_column] = tmp_data; } else { processed_data[i][last_column] = processed_data[i][last_column-1]; } } for(j = 0; j< 2*W-1; j++)//insert values into last line { if((tmp_data = 2*processed_data[last_line-1][j] - processed_data[last_line-2][j]) > 0) { processed_data[last_line][j] = tmp_data; }else { processed_data[last_line][j] = processed_data[last_line-1][j]; } } processed_data[last_line][last_column] = (processed_data[last_line-1][last_column] + processed_data[last_line][last_column-1] + processed_data[last_line-1][last_column-1])/3; return 1; }
本段程序实现了函数形参为二维数组的传递,以及二维数组在堆的动态内存分配。我当初直接为二维数组在栈上分配空间,也就是指定死二维数组的行和列,但是当分辨率较高时,在linux编译能通过,但是运行遇到段错误(吐核),如下
目前也没有找到其根本原因,后面用二维数组的动态内存分配解决了这个问题……
2.2程序的测试结果
截取的数据为放大前、后图片中间的某块数据,平均运行时间为0.098260秒
3.1插值函数参数为一维数组
紫色框的圆代表原始数据,2个for循环实现,首先左右取均值插值(×),再上下取均值插值(√);也可以在插值子函数里实现动态内存,但是为了方便管理,尽量在主函数分配,谁用谁分配和释放,只需要调用函数接口即可。
源码如下:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <sys/time.h> typedef unsigned short dbyte; #define M 1536 //原始图片高度 #define N 2048 //原始图片宽度 #define H 3072 //height 放大2倍图片的高度 #define W 4096 //weight 放大2倍图片的宽度 dbyte* insert_values(dbyte *original_data, dbyte *processed_data); int main(int argc, int *argv[]) { int i, count; //内存分配 dbyte *original_data = (dbyte *)malloc(M*N*sizeof(dbyte)); dbyte *processed_data = (dbyte *)malloc(W*H*sizeof(dbyte)); if(original_data == NULL) { printf("please allocate the input memery again!\n"); } if(processed_data == NULL) { printf("please allocate the output memery again!\n"); } //初始化内存为零 memset(original_data,0,M*N); memset(processed_data,0,W*H); //the test program as the follows. //set original data for(i = 0; i < N*M; i++) { original_data[i] = rand(); } //printf have not be to insert for(i = 238*N + 320, count = 1; i < 324 + 241* N; ) { printf(" %d__%ld ",i, original_data[i]); if(count%4 == 0) { count = 1; i += (N-3); printf("\n"); }else{ count++; i++; } } printf("______________*****************_____________________________*****************__________\n"); struct timeval tpstart, tpend; float timeuse; gettimeofday(&tpstart, NULL); insert_values(original_data,processed_data);//use of the function gettimeofday(&tpend, NULL); //printf insert have been down for(i = 476*W + 640, count = 1; i < 648 + 481* W; ) { printf(" %d__%ld ", i, processed_data[i]); if(count%8 == 0) { count = 1; i += (W-7); printf("\n"); }else{ count++; i++; } } timeuse = 1000000*(tpend.tv_sec - tpstart.tv_sec)+ tpend.tv_usec - tpstart.tv_usec; timeuse /= 1000000; printf("The usetime of One-dimensional array is %fS\n",timeuse); free(processed_data); free(original_data); } dbyte* insert_values(dbyte *original_data, dbyte *processed_data)//The value of return can be define by yourself { int i, j; short tmp; //set_values from original_data to the processed_data for(i = j = 0; i<W*(H-1); i += 2, j++)//i为循环要放大的图像,步长为2(隔列插入),j用来循环遍历原始数据 { if((i!= 0) && (i%W) == 0)//判断是否把一个W宽度的数据插完,真就跳W个数据继续插值,注意:如果不添加i!=0,当i=0,只判断i%W==0,最开始的W个数据时没有的 { i += W; } processed_data[i] = original_data[j]; } //insert_values into the line caculating by left number and right number 左右均值插值 for(i = 1;i < W*(H-1); i += 2) { if((i+1)%W == 0)//最后一列的插值方法,图像由左向右延伸 { if((tmp = processed_data[i-1]*2-processed_data[i-2]) > 0) //如果中间值的2倍大于两边值之和,就插2倍中间值减去已知的值 { processed_data[i] = tmp; } else{ processed_data[i] = processed_data[i-1];//如果需要插的值是小于零的,直接复制前一个像素点数据 } i += W; } else { processed_data[i] = (processed_data[i-1] + processed_data[i+1])/2; } } //insert_values into the line caculating by up number and down number for(i = W; i < W*H; i++) { if(i < W*(H-1)) { processed_data[i] = (processed_data[i-W] + processed_data[i+W])/2; if((i+1)%W == 0) { i += W; } }else//最后一行的插值方法,图像由上向下延伸 { if((tmp = processed_data[i-W]*2-processed_data[i-2*W]) > 0) { processed_data[i] = tmp; }else{ processed_data[i] = processed_data[i-W]; } } } return processed_data; }
3.2一维数组程序的测试结果
截取的数据为放大前、后图片中间的某块数据,平均运行时间为0.081015秒
4.1结论
(0.098260-0.081015)=0.17245秒0.17245/0.09826*100=17.55%,效率提高了百分之17.55。可能有的人觉得没有必要,毕竟现在的CPU处理速度已经相当快了。但如果是在嵌入式设备上就不一定了,毕竟嵌入式设备的资源都是有限的,所以很宝贵,充分利用它才是王道。
相关文章推荐
- C++重载、覆盖、隐藏
- 二叉树的镜像 C++
- C语言的类型,字节,还有转义字符0728
- c++pair(类似struct)用法
- C/C++运维中发现的两个隐晦BUG
- C++的cin一些探讨
- c++计算器后续(3)
- C语言中,为什么字符串可以赋值给字符指针变量
- 1. Two Sum(c++实现)
- codeblocks编译出错问题的解答!(编译c++ 或者c程序)
- 在C语言中,double、long、unsigned、int、char类型数据所占字节数
- c++set常用用法
- c++内存对齐
- 求一元二次方程的根
- 黑马程序员:C++学科推出全新三大实战项目
- Cpp环境【POJ3622】【Vijos2990】【Usaco2007】挑剔的美食家
- 【OpenJudge】c语言_计算2的幂
- 【OpenJudge】c语言_A*B问题
- 【OpenJudge】c语言_计算三角形面积
- C语言词法分析:C#源码