openCV入门----单窗口多图片显示

    在通过图像显示入门之后,自然想到多图像单窗口显示如何实现,有时候往往要对比几幅图来加以对比,从而更直观地看到处理效果!在Matlab里就很容易做了,如果是显示多个曲线图,那么subplot函数秒秒钟帮你解决问题!但是C++中使用openCV函数,那可能得费一点功夫了!
     话不多说,直接上代码!
#include<iostream>
#include <stdarg.h>
#include"windows.h"
#include"stdio.h"
#include"cv.h"
#include"highgui.h"
#include"cxcore.h"
using namespace std;
// 隐藏 console 窗口
//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")

// 单窗口显示多幅图像的函数
void cvShowMultiImages(char* title, int nArgs, ...)
{
// img - Used for getting the arguments
IplImage* img;
// DispImage - the image in which all the input images are to be
//copied
IplImage* DispImage;

int size; // size - the size of the images in the window
int ind;  // ind - the index of the image shown in the window
int x, y;  // x,y - the coordinate of top left coner of input images
int w, h;    // w,h - the width and height of the image

// r - Maximum number of images in a column
// c - Maximum number of images in a row
int r, c;

// scale - How much we have to resize the image
float scale;
// max - Max value of the width and height of the image
int max;
// space - the spacing between images
int space;

// If the number of arguments is lesser than 0 or greater than 12
// return without displaying
if (nArgs <= 0) {
printf("Number of arguments too small..../n");
return;
}
else if (nArgs > 12) {
printf("Number of arguments too large..../n");
return;
}
// Determine the size of the image,
// and the number of rows/cols
// from number of arguments
else if (nArgs == 1) {
r = c = 1;
size = 300;
}
else if (nArgs == 2) {
r = 2; c = 1;
size = 300;
}
else if (nArgs == 3 || nArgs == 4) {
r = 2; c = 2;
size = 300;
}
else if (nArgs == 5 || nArgs == 6) {
r = 3; c = 2;
size = 200;
}
else if (nArgs == 7 || nArgs ==8 ) {
r = 4; c = 2;
size = 200;
}
else {
r = 4; c = 3;
size = 150;
}

// Create a new 3 channel image to show all the input images
DispImage = cvCreateImage(cvSize(60 + size*r, 20 + size*c), IPL_DEPTH_8U, 3);[0]
// Used to get the arguments passed
va_list args;[1]
va_start(args, nArgs);

// Loop for nArgs number of arguments
space = 20;
for (ind = 0, x = space, y = space; ind < nArgs; ind++, x += (space + size)) {

// Get the Pointer to the IplImage
img = va_arg(args, IplImage*);

// Check whether it is NULL or not
// If it is NULL, release the image, and return
if (img == 0) {
printf("Invalid arguments");
cvReleaseImage(&DispImage);
return;
}

// Find the width and height of the image
w = img->width;
h = img->height;

// Find whether height or width is greater in order to resize the image
max = (w > h) ? w : h;

// Find the scaling factor to resize the image
scale = (float)((float)max / size);

// Used to Align the images
// i.e. Align the image to next row
if (ind % r == 0 && x != space) {
x = space;
y += space + size;
}

               // Set the image ROI to display the current image
cvSetImageROI(DispImage, cvRect(x, y, (int)(w / scale), (int)(h / scale)));[2]
     // Resize the input image and copy the it to the Single Big Image
cvResize(img, DispImage);[3]
// Reset the ROI in order to display the next image
cvResetImageROI(DispImage);[4]
}

// Create a new window, and show the Single Big Image
//cvNamedWindow( title, 1 );
cvShowImage(title, DispImage);
// End the number of arguments
va_end(args);
// Release the Image Memory
cvReleaseImage(&DispImage);
}

int main()
{
/*   CvCapture *capture;*/
int i = 0;
IplImage* frame = cvLoadImage("8.png");
cvNamedWindow("video");
cvResizeWindow("video", 700, 660);
IplImage *frame_not = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
cvNot(frame, frame_not);
IplImage *frame_gray = cvCreateImage(cvGetSize(frame), frame->depth, 1);
IplImage *frame1 = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
IplImage *frame_canny = cvCreateImage(cvGetSize(frame), frame->depth, 1);
IplImage *frame2 = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
cvCvtColor(frame, frame_gray, CV_RGB2GRAY);[5]
cvCvtColor(frame_gray, frame1, CV_GRAY2BGR);
cvCanny(frame_gray, frame_canny, 20, 75, 3);[6]
cvCvtColor(frame_canny, frame2, CV_GRAY2BGR);
cvShowMultiImages("video", 3, frame, frame_not, frame2);

cvWaitKey();

cvReleaseImage(&frame_not);
cvReleaseImage(&frame1);
cvReleaseImage(&frame_gray);
cvReleaseImage(&frame2);
cvReleaseImage(&frame_canny);

cvDestroyWindow("video");

return 0;
}

效果如下:




正如代码中红色部分所示,需要注意一下几个函数或者知识点:
[0]:cvCreateImage是openCV中的一个函数。     

cvCreateImage:创建头并分配数据
  IplImage* cvCreateImage( CvSize size, int depth, int channels );
  参数说明:
  size 图像宽、高.这里涉及到一个专门的类Cvsize(int h,int w).
  depth 图像元素的位深度,可以是下面的其中之一:
  IPL_DEPTH_8U - 无符号8位整型
  IPL_DEPTH_8S - 有符号8位整型
  IPL_DEPTH_16U - 无符号16位整型
  IPL_DEPTH_16S - 有符号16位整型
  IPL_DEPTH_32S - 有符号32位整型
  IPL_DEPTH_32F - 单精度浮点数
  IPL_DEPTH_64F - 双精度浮点数
  channels:
  每个元素(像素)通道号.可以是 1, 2, 3 或 4.通道是交叉存取的,例如通常的彩色图像数据排列是:b0 g0 r0 b1 g1 r1 ... 虽然通常 IPL 图象格式可以存贮非交叉存取的图像,并且一些OpenCV 也能处理他, 但是这个函数只能创建交叉存取图像.
[1]:va_list
这是C语言在解决函数多变量参数问题时定义的一组宏。由va_start()和va_end()界定。
    typedef char* va_list;
  void va_start ( va_list ap, prev_param );
  type va_arg ( va_list ap, type );
  void va_end ( va_list ap );
其中,va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
使用步骤如下:
    <Step 1> 在调用参数表之前,应该定义一个 va_list 类型的变量,以供后用(假设这个 va_list 类型变量被定义为ap);
    <Step 2> 然后对 ap 进行初始化,让它指向可变参数表里面的第一个参数。这是通过 va_start 来实现的,其第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量;
    <Step 3> 然后是获取参数,调用 va_arg。它的第一个参数是 ap,第二个参数是要获取的参数的指定类型,并返回这个指定类型的值,同时把 ap 的位置指向变参表的下一个变量位置;
    <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end。它是将输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。
这还是比较抽象,那么上代码来解决问题吧!
#include<iostream>
#include <stdarg.h>
using namespace std;
/*
这是一个va_list测试函数
*/
int Sum(int n,...)//这里的n表示后面变量的参数,定界用
{
va_list ap;
va_start(ap, n);
int sum = 0;
for (int i = 0;i < n;i++)
{
sum = sum + va_arg(ap, int);
}
va_end(ap);
return sum;
}
int main()
{
cout << Sum(5, 1, 1, 1, 1, 1) << endl;
getchar();
}
我们定义了sum这个函数,想把若干数求和再输出来,那么sum中第一个参数便是你真正要输入的参数个数。最后这个程序运行的结果不言而喻:5.注意:va_list宏是包含在头文件stdarg.h中的,不要忘记了!
[2]:cvSetImageROI和cvRect函数
基于给定的矩形设置图像的ROI(感兴趣区域,region of interesting)大多数OpenCV函数都支持ROI,并将它作为一个独立图像进行处理,所有像素坐标都是从ROI的左上角或者左下角(基于图像结构)开始计算的。cvSetImageROI(img1,cvRect(100,100,356,156)),(100,100)表示ROI区域的左上角坐标,356,156分别表示ROI区域的长宽。
[3]:cvResize:
void cvResize( const CvArr* src, CvArr* dst, int interpolation=CV_INTER_LINEAR );
主要功能:重新调整图像src(或它的ROI),使它精确匹配目标dst(或其ROI)。这里需要说明的是,cvResize可以用来调整3通道图像(如RGB图像)和单通道图像的大小。
src: 源图像
dst :目标图像
interpolation 修改、插补的方法,取值如下:
·CV_INTER_NN - 最近-邻居插补
·CV_INTER_LINEAR - 双线性插值(默认方法)
·CV_INTER_AREA - 像素面积相关重采样。当缩小图像时,该方法可以避免波纹的出现。当放大图像时,类似于方法CV_INTER_NN。(It is the preferred method for image decimation that gives moire-free results. In case of zooming it is similar to CV_INTER_NN method. )
·CV_INTER_CUBIC - 双三次插值。
[4]:cvResetImageROI
功能: 释放基于给定的矩形设置图像的ROI(感兴趣区域,region of interesting)
格式: void cvResetImageROI(IplImage* image)
参数: image 图像头,待处理图像
说明: 释放图像image中被设定的感兴趣区域ROI,与cvSetImageROI相对应。相对应这点很重要!
[5]:cvCvtColor函数
void cvCvtColor( const CvArr* src, CvArr* dst, icvcvtcolor
cvcvtcolornt code );
src:输入的 8-bit,16-bit或 32-bit单倍精度浮点数影像名。
dst:输出的8-bit, 16-bit或 32-bit单倍精度浮点数影像。
code:色彩空间转换的模式,该code来实现不同类型的颜色空间转换。比如CV_BGR2GRAY表示转换为灰度图,CV_BGR2HSV将图片从RGB空间转换为HSV空间。其中当code选用CV_BGR2GRAY时,dst需要是单通道图片。当code选用CV_BGR2HSV时,对于8位图,需要将RGB值归一化到0-1之间。这样得到HSV图中的H范围才是0-360,S和V的范围是0-1,CV_RGB2Lab转换RGB或者BGR色彩空间到CIE LAB色彩空间或者反变换等RGB与 BGR或者HLS等的互相之间转换。
图像色彩空间的转换后续会补充说明,此处只做了解。
[6]:cvCanny函数
void cvCanny( const CvArr* image,CvArr* edges,double threshold1,double threshold2, int aperture_size=3 );
用途:用于图像的边缘检测
image:输入图像,这个必须是单通道的,即灰度图
edges:​输出的边缘图像 ,也是单通道的,但是是黑白的
threshold1:第一个阈值
​threshold2:第二个阈值
aperture_size Sobel:算子内核大小
函数 cvCanny 采用 Canny 算法发现输入图像的边缘而且在输出图像中标识这些边缘。threshold1和threshold2 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。
Canny算法是边缘检测算法的一种,明天我将进行一定的测试,这里先只了解!


思路整理:不知道大家看了这个代码之后有没有这个感觉,多个图像单窗口显示实际上就想Java里JFrame、JPanne、控件之间的关系,先划分一个大的区域,在利用坐标和区域大小关系来分割区域放置图片。

猜你喜欢

转载自lps-683.iteye.com/blog/2249725