前几天在遇到一个问题:将一个图片读入到内存中,然后自己操作数组(自己声明)元素来实现图像的上下、左右翻转。
下面是具体的要求:
{
/***************************************************************************************************************************
下面的代码已经将一幅图像“Fruits.jpg”读入到内存中。已知图像的大小是512*480像素,每个像素的类型是一个unsigned char的整数(即在区间[0,255]内)。数据存储在指针img_data指向的内存区,每一行所占的字节数是width_step。
请编程实现:
1. 判断width_step是否等于列数*每个元素所占字节数。
2. 声明二维数组a,将图像数据读入a中。即实现函数ReadImageData。
3. 将数组a的元素上下翻转。即第一行变为最后一行。即实现函数FlipImageUpDown。
4. 将变换后的数组a再写回img_data所指的内存里。即实现函数WriteImageData。如果以上步骤正确,会显示翻转后的图像。
5. 将数组a的元素左右翻转。即第一列变为最后一列。即实现函数FlipImageLeftRight。
6. 将变换后的数组a再写回img_data所指的内存里。即调用函数WriteImageData。如果以上步骤正确,会显示翻转后的图像。
将图像缩小为原来尺寸的一半,存入动态分配内存的二维数组b。一个简单的做法是将a中的属于奇数行和奇数列的元素读取写入到b中。
**************************************************************************************************************************/
}
在读入图片的步骤,我们使用了opencv库。这个问题需要考虑以下几个问题:
Note :
1.oenpcv + vc6.0 开发环境的配置
2.IplImage结构体的 成员
3.读入的图像数据时如何在内存中存放的(注意行与列,一维)
4.char ** &dst 与char ** dst的区别(二维数组如何作为函数参数传递)
5.二维数组的动态分配(动态分配的内存空间一定要释放掉)
6.如何操作数组实现图像的上下、左右翻转。
那么现在咱们先解决上面五个问题:
1.opencv库的安装可参考网上的教程,这里提一点,如果用VC6.0开发的话,需要在project里面手动添加一些库。
project---->setting---->link---->object/libary modul 添加cxcore.lib cv.lib ml.lib cvaux.lib highgui.lib cvcam.lib
2.IplImage结构体的成员有很多,我们只使用到了下面几个:
int width; /* 图像宽像素数 */
int height; /* 图像高像素数*/
int widthStep; /* 排列的图像行大小,以字节为单位 */
char *imageData; /* 指向排列的图像数据 */
3.读入的图像数据是如何存放的,这一点是至关重要的,因为你要把内存中的图像数据取到自己定义的二维数组中,
opencv库函数读入的图像数据在内存中的存储方式是一维的(虽然图像的灰度数据是二维的),我们需要知道二维数组的行跟列存放元素的个数以及我们如何从内存中的一维数据变化为二维。
假设一个小型矩阵来表示图像数据:
1 2 3
4 5 6
那么它在内存中时这样的:1 2 3 4 5 6 ,按行主序优先的顺序。(到这你应该知道怎么向二维数组里面取了吧)
width表示宽度,即一行存放多少个元素,height表示高度,表示有多少行。
4.虽然在本问题的实验中没有具体的实现要求,但是我在coding时遇到了这个问题。目前还没有解决。
5.二维数组的动态分配一般有两种方式,在这里我们选择了一次性分配所有存储空间的方式(即二维数组的地址在内
存中是连续的),这里需要强调一点:动态声明的内存一定要显式的释放掉,可以在动态声明的地方标示一下。
// 声明二维数组a,大小是512*480,unsigned char类型;
unsigned char **arrayofimg;
arrayofimg = new unsigned char *[height]; arrayofimg[0] = new unsigned char[height * width];
for (int i = 1; i < height; ++i){ //这是在分配每一行的地址
arrayofimg[i] = arrayofimg[i-1] + width;
}//释放空间,从里向外释放
delete [] arrayofimg[0]; // 先释放内层
delete [] arrayofimg; //然后释放外层
6.上下翻转只要将二维数组的行翻转即可,例如:array[i][j] = array[rows-i-1][j];
array[i][j] = array[i][cols-j-1];
下面我们来看具体实现的代码:
/*
Author By : Yumaosheng
Number : 21140211080
Note :
1.oenpcv + vc6.0 开发环境的配置
2.IplImage结构体的 成员
3.读入的图像数据时如何在内存中存放的(注意行与列,一维)
4.char ** &dst 与char ** dst的区别(二维数组如何作为函数参数传递)
5.二维数组的动态分配(动态分配的内存空间一定要释放掉)
*/
#include <stdio.h>
#include <new>
#include "cv.h"
#include "highgui.h"
//读取内存中的图像数据,存储在用户自己定义的数组dst中
//read the img data from the Memory to own array
void ReadImageData(unsigned char *src, int rows, int cols, int width_step, unsigned char ** &dst){
int count = 0;
for (int i = 0; i < cols; ++i){
for (int j = 0; j < rows; ++j){
// printf("%c\n", *((unsigned char*)dst + rows * i + j));
// *((unsigned char*)dst + rows * i + j) = src[count];
dst[i][j] = src[count];
count++;
}
}
printf("%d\n", count);
return;
}
//将用户自己数组存储的输入拷贝到图像相应的内存位置
//write the img data ( my array) to the memory
void WriteImageData(unsigned char *src, int rows, int cols, int width_step,unsigned char ** &dst, bool flag){
int count =0;
for (int i = 0; i < cols; ++i){
for (int j = 0; j < rows; ++j){
if(flag) src[count] = dst[i][j];
else src[count] = (unsigned char)'a';
count++;
}
}
printf("%d\n", count);
return;
}
//实现图像的上下翻转
//Up Down the img
void FlipImageUpDown(unsigned char ** &dst,int rows, int cols){
unsigned char temp = NULL;
for (int i = 0; i < cols/2; i++){ // swap the rows Only
for (int j = 0; j < rows ; j++){
temp = dst[i][j];
dst[i][j] = dst[cols-1-i][j];
dst[cols-1-i][j] = temp;
// dst[i][j] = dst[i][cols-j-1];
}
}
return;
}
//实现图像的左右翻转
//Left and Right the img
void FlipImageLeftRight(unsigned char ** &dst, int rows, int cols){
unsigned char temp = NULL;
for (int i = 0; i < cols; ++i){ //swap the cols
for (int j = 0; j < rows/2; ++j){
temp = dst[i][j];
dst[i][j] = dst[i][rows-1-j];
dst[i][rows-1-j] = temp;
}
}
return;
}
int main(int args ,char *argv[]){
//采用灰度模式将图片读入内存
IplImage* img = cvLoadImage("Fruits.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//创建显示图片的窗口
cvNamedWindow("Image", CV_WINDOW_AUTOSIZE);
cvShowImage("Image", img);
cvWaitKey(0); //停屏 等待用户的键盘输入
unsigned char *img_data = (unsigned char *)(img->imageData);
// unsigned char tmp = *(img_data + 1);
// printf("%d %d\n", tmp , img_data[1]);
int height = img->height;
int width = img->width;
int width_step = img->widthStep;
//(1)判断width_step是否等于列数*每个元素所占字节数
// 图片为512*480,有512列,每一个像素为8bit,所以width_step的值应该为512*1Byte;
if(width_step == 512) printf("%d\n", width_step);
// 声明二维数组a,大小是512*480,unsigned char类型;
unsigned char **arrayofimg;
arrayofimg = new unsigned char *[height];
arrayofimg[0] = new unsigned char[height * width];
for (int i = 1; i < height; ++i){
arrayofimg[i] = arrayofimg[i-1] + width;
// printf("%d\n", i);
}
//测试动态分配的二维数组是否成功 分配成功
/*
int sum = 0;
for (int m = 0; m < height; ++m){
for (int n = 0; n < width; ++n){
// printf("%c ", *((unsigned char*)arrayofimg + m * width + n) );
arrayofimg[m][n] = (unsigned char)'a';
// printf("%c", arrayofimg[m][n]);
sum++;
}
//printf("\n");
}
printf("the sum of the piex is : %d \n",sum);
*/
// 读取图像数据到二维数组a中,实现函数ReadImageData
// 比如这样调用ReadImageData(img_data, 512, 480, width_step, a);
/*
printf("wait for input:\n");
printf("1----FlipImageUpDown\n");
printf("2----FlipImageLeftRight\n");
int input;
while(scanf("%d", &input) !=EOF){
ReadImageData(img_data, width, height, width_step, arrayofimg);
if (input == 1){
printf("FlipImageUpDown!!!\n");
FlipImageUpDown(arrayofimg, width, height);
WriteImageData(img_data, width, height, width_step, arrayofimg,true);
//create the window for show the photo
// cvNamedWindow("Image", CV_WINDOW_AUTOSIZE);
cvShowImage("Image", img);
// cvWaitKey(0); //停屏 等待用户的键盘输入
}
if (input == 2){
printf("FlipImageLeftRight!!!\n");
FlipImageLeftRight(arrayofimg, width, height);
WriteImageData(img_data, width, height, width_step, arrayofimg,true);
//create the window for show the photo
// cvNamedWindow("Image", CV_WINDOW_AUTOSIZE);
cvShowImage("Image", img);
// cvWaitKey(0); //停屏 等待用户的键盘输入
}
}
*/
ReadImageData(img_data, width, height, width_step, arrayofimg);
FlipImageUpDown(arrayofimg, width, height);
// FlipImageLeftRight(arrayofimg, width, height);
WriteImageData(img_data, width, height, width_step, arrayofimg,true);
cvShowImage("Image", img);
cvWaitKey(0); //等待按键
ReadImageData(img_data, width, height, width_step, arrayofimg);
FlipImageLeftRight(arrayofimg, width, height);
// FlipImageLeftRight(arrayofimg, width, height);
WriteImageData(img_data, width, height, width_step, arrayofimg,true);
cvShowImage("Image", img);
cvWaitKey(0); //等待按键
//测试图像数据是否已读入自己的数组中
//读入成功!
/*
int sum =0 ;
for (int m = 0; m < width; ++m){
for (int n = 0; n < height; ++n){
// printf("%c ", *((unsigned char*)arrayofimg + m * width + n) );
//printf("%d ", arrayofimg[m][n]);
sum++;
}
// printf("\n");
}
printf("%d\n", sum);
*/
cvDestroyWindow("Image");//销毁窗口
cvReleaseImage(&img); //释放图像
delete [] arrayofimg[0];
delete [] arrayofimg;
return 0;
}
这段代码的风格还是C的风格,以后还需要进行封装,敬请关注我的下一篇博文。