vs 运行opencv的sample

opencv提供的源码里提供了很多的sample供我们学习。现在用vs跑一下其中的c++的sample代码,方便以后学习opencv和c++

为了方便起见,我下载的opencv是3.3.1的windows的安装包,而没有从源码进行编译。安装包打开后会自动解压到指定位置。该安装包中包含了源码和已经编译好的库。打开opencv的根目录,可以看到两个文件夹,build和sources,其中build中为已经编译好的库,而sources中为源码。打开sources,可以看到很多文件夹,其中modules为opencv的核心源码。3rdparty中为一些第三方库的源码。而samples中则是包含的opencv的示例代码,我们要演示的就是其中cpp文件夹下,tutorial_code中的HighGUI。它是一个具有UI的,可以调整图片对比度和亮度的示例。

打开vs,选择文件——新建——从现有代码创建项目,选择Visual C++,下一步,项目文件位置找到之前所述的HighGUI文件夹,然后输入一个项目名称,例如highgui,然后一直下一步,完成。vs会创建项目的解决方案。该文件夹下包含两个cpp文件,每个文件都有main函数。因此我们选择其中一个运行。选择BasicLinearTransformsTrackbar.cpp,需要在解决方案资源管理器中,右键另一个cpp文件AddingImagesTrackbar,从项目中排除。然后打开Basic LinearTransformsTrackbar.cpp 文件,可以看到有很多红色波浪线。因为还没对项目进行配置。选择项目-属性,默认的配置是Debug,win32 ,由于所提供的库只有x64的,因此这里要选择x64 。注意运行时也要改成x64。

选择c/c++,常规,在附加包含目录中,添加所需的头文件的目录。这里添加build目录下的include,include\opencv,include\opencv2。然后选择链接器,常规,附加库目录中,选择所需的lib库的目录,这里添加build目录下x86\vc14\lib。然后选择链接器,输入,附加依赖项,添加该目录下的lib文件。我这里是opencv_world331.lib和opencv_world331d.lib 。最后选择调试,在环境中,添加运行时所需dll的目录,这里的添加方式为Path=(opencv根目录)\build\x64\vc14\bin。然后工作目录,为上两级文件夹。(根据代码中读取图片../data/lena.jpg可以判断)

配置完成后点击应用就可以了。然后就可以点击F5运行。注意运行前配置管理器要改成x64。我们可以配置release版的,步骤与上述相同。运行成功后可以在release文件夹下找到exe文件。将该exe文件,还有运行需要的dll、一些资源文件拷贝到独立的文件夹,就可以当作独立的软件发布了。

在jni函数中使用传入的bitmap

在AS中,有时会要传递bitmap给c++函数,使用opencv等工具进行处理。这时会将bitmap传递给jni函数。bitmap在jni的函数参数中,是jobject类型。下面介绍如何将传入的bitmap转化成opencv常用的mat类型,从而进行处理

首先要包含bitmap的头文件<android/bitmap.h>。这个头文件中定义了一些重要信息。了解这些信息非常必要

AndroidBitmapInfo 一个包含bitmap常用信息的结构体

/** Bitmap info, see AndroidBitmap_getInfo(). */
typedef struct {
/** The bitmap width in pixels. */
uint32_t width;
/** The bitmap height in pixels. */
uint32_t height;
/** The number of byte per row. */
uint32_t stride;
/** The bitmap pixel format. See {@link AndroidBitmapFormat} */
int32_t format;
/** Unused. */
uint32_t flags; // 0 for now
} AndroidBitmapInfo;

AndroidBitmapFormat 图片格式的枚举类型。格式为图片的像素位数。最常见的是ANDROID_BITMAP_FORMAT_RGBA_8888

/** Bitmap pixel format. */
enum AndroidBitmapFormat {
/** No format. */
ANDROID_BITMAP_FORMAT_NONE = 0,
/** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
/** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
ANDROID_BITMAP_FORMAT_RGB_565 = 4,
/** Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead. **/
ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
/** Alpha: 8 bits. */
ANDROID_BITMAP_FORMAT_A_8 = 8,
};

AndroidBitmap_getInfo 获取bitmap信息的函数。返回值为后面的枚举类型。成功读取的返回值为0

/**
* Given a java bitmap object, fill out the AndroidBitmapInfo struct for it.
* If the call fails, the info parameter will be ignored.
*/
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info);
/** AndroidBitmap functions result code. */
enum {
/** Operation was successful. */
ANDROID_BITMAP_RESULT_SUCCESS = 0,
/** Bad parameter. */
ANDROID_BITMAP_RESULT_BAD_PARAMETER = -1,
/** JNI exception occured. */
ANDROID_BITMAP_RESULT_JNI_EXCEPTION = -2,
/** Allocation failed. */
ANDROID_BITMAP_RESULT_ALLOCATION_FAILED = -3,
};

AndroidBitmap_lockPixels 非常关键的一个函数。将bitmap的像素在内存中的地址进行锁定,确保这一块内存不会被移动。传入的addrPtr是一个二级指针,*addPtr是指向bitmap像素地址的指针。该函数正确调用后,一定要在该像素地址指针不再使用后,使用AndroidBitmap_unlockPixels进行释放。

/**
* Given a java bitmap object, attempt to lock the pixel address.
* Locking will ensure that the memory for the pixels will not move
* until the unlockPixels call, and ensure that, if the pixels had been
* previously purged, they will have been restored.
*
* If this call succeeds, it must be balanced by a call to
* AndroidBitmap_unlockPixels, after which time the address of the pixels should
* no longer be used.
*
* If this succeeds, *addrPtr will be set to the pixel address. If the call
* fails, addrPtr will be ignored.
*/
int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);

AndroidBitmap_unlockPixels

/**
* Call this to balance a successful call to AndroidBitmap_lockPixels.
*/
int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);

了解以上信息之后,就可以探索如何将传入的bitmap转为需要的mat了。通用的做法是先获取bitmap的信息

AndroidBitmapInfo img_info;
CV_Assert(AndroidBitmap_getInfo(env, bitmap, &img_info) >= 0);
CV_Assert(img_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);

使用CV_Assert确保获取信息成功

然后初始化一个指针,并将该指针的地址传递给AndroidBitmap_lockPixels,若成功则该指针即指向bitmap的像素在内存中的地址

void *ori_img_pxl = NULL;
CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &ori_img_pxl) >= 0);
CV_Assert(ori_img_pxl);

然后就可以将该指针传递给mat的构造函数,创建出我们需要的mat了。

Mat ori_img_mat(img_info.height, img_info.width, CV_8UC4, ori_img_pxl);

需要注意得到的mat的数据指针是指向bitmap的像素内存的,因此mat中对像素的操作会直接反映在bitmap中

AndroidBitmap_unlockPixels(env, bitmap);

最后要记得释放锁定!