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插值函数参数为二维数组

java二维线性插值 二维线性插值c语言_一维数组


紫色框的圆代表原始数据,首先上下取均值插(√),再左右取均值插(×),再取四个均值插(※),最后插最后一列和最后一行。数据来源于计算机生成的伪随机数,为了方便测试该程序,改用较大分辨率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编译能通过,但是运行遇到段错误(吐核),如下

java二维线性插值 二维线性插值c语言_c语言_02


目前也没有找到其根本原因,后面用二维数组的动态内存分配解决了这个问题……

2.2程序的测试结果

java二维线性插值 二维线性插值c语言_二维数组_03

截取的数据为放大前、后图片中间的某块数据,平均运行时间为0.098260秒

3.1插值函数参数为一维数组

java二维线性插值 二维线性插值c语言_一维数组_04


紫色框的圆代表原始数据,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一维数组程序的测试结果

java二维线性插值 二维线性插值c语言_c语言_05

截取的数据为放大前、后图片中间的某块数据,平均运行时间为0.081015秒

4.1结论

(0.098260-0.081015)=0.17245秒
0.17245/0.09826*100=17.55%,效率提高了百分之17.55。可能有的人觉得没有必要,毕竟现在的CPU处理速度已经相当快了。但如果是在嵌入式设备上就不一定了,毕竟嵌入式设备的资源都是有限的,所以很宝贵,充分利用它才是王道。