python和C++混合编程实现区域增长
这两天项目需要选取眼底图片的感兴趣区域,这个图片长这个样子:
这个图片中除了黑色部分都是感兴趣区域,而黑色背景部分是一块连续的区域,用区域增长的方法能很快确定,于是我就拿matlab实现了一下,效果很nice,是我想要的内容。我的平台是在pyQT5上搭建的,怀着激动的心情用python按照matlab的思路写了一遍,其具体代码如下:
ef region_grow(I,seed_x,seed_y):
'''
The ALG of region_grow is based on binary images
:param I:
:param seed_x:
:param seed_y:
:return:
'''
high, width = np.shape(I)
result_old = np.zeros([high, width])
result = result_old.copy()
result[seed_x,seed_y] = 1
while np.max(np.abs(result-result_old)) > 0: #前一次迭代结果不等于后一次需要不断迭代
result_old = result.copy()
for i in range(1,high - 1):
for j in range(1,width - 1):
if result[i,j] == 1:
if I[i,j - 1] == 1:
result[i,j - 1] = 1
if I[i,j + 1] == 1:
result[i,j + 1] = 1
if I[i - 1,j - 1] == 1:
result[i - 1,j - 1] = 1
if I[i - 1,j] == 1:
result[i - 1,j] = 1
if I[i - 1,j + 1] == 1:
result[i - 1 ,j + 1] = 1
if I[i + 1,j - 1] == 1:
result[i + 1,j - 1] = 1
if I[i + 1,j] == 1:
result[i + 1,j] = 1
if I[i + 1,j + 1] == 1:
result[i + 1,j + 1] = 1
return result
然后开心的点了运行按钮,拿起了我的水杯,喝了一口茶,咦~怎么还没反应,是不是喝的不够,再来一口,怎么还是没反应,我就继续一口一口的喝起了茶,终于我喝完了茶,但是它怎么还没有反应,是不是我写了死循环,吓得我赶紧停下来去查看,结果发现并不是我的错。作为一个自负的菜狗,就又点了运行,这次我倒了更大的一杯茶,终于在45秒之后,给我弹出了我想要的结果,如下图所示。
这让我很生气,明明一样的算法,为啥比matlab慢这么多呢,我细细看了下我的代码,有很多if,我猜想是不是这个问题,于是我测试了matlab和python的if语句的速度,matlab的if语句的运行速度在我的电脑上大约是python的15-20倍的样子,让我感到挺不可思议的。
越想越气,立志一定要解决这个问题,坚决不能让我每次都等45秒。所以我决定用python调用C++函数,希望能加快我的速度。我参考了这位兄台的内容。但是我用的C++的IDE是QT,下面记录了我整个操作的过程。
一、生成DLL
1.打开QT Creator,新建文件文件或项目,选择Library->C++ library.
然后一路默认就可以,当然可以改下自己的存储路径名字等。我才用Cmake进行的,完成之后的文件结果如下图所示.
2.修改头文件(Region_grow.h)
#ifndef REGION_GROW_H
#define REGION_GROW_H
#include <iostream>
#include <math.h>
#include "Region_Grow_global.h"
using namespace std;
#if defined(REGION_GROW_LIBRARY)
//让DLL文件的函数可以检测到
# define REGION_GROW_EXPORT Q_DECL_EXPORT
#else
# define REGION_GROW_EXPORT Q_DECL_IMPORT
#endif
// C++中不能直接返回二维数组,可以借助结构体实现
typedef struct Image
{
int I[1024][1024];
}image, *Im;
class REGION_GROW_EXPORT Region_Grow
{
public:
Region_Grow();
};
#endif // REGION_GROW_H
//被外部函数能检测到的函数的声明
extern "C" REGION_GROW_EXPORT Im region_grow(int a[1024][1024],int seed_x,int seed_y);
//产生零矩阵
int ** zeros(int high,int width);
//矩阵的复制
Im copy(Im sorce, Im target);
//查看矩阵是不是相同
bool different(int f[1024][1024],int s[1024][1024]);
2. 实现头文件中的函数(修改.cpp文件)
#include "region_grow.h"
Region_Grow::Region_Grow()
{
}
REGION_GROW_EXPORT Im region_grow(int I[1024][1024],int seed_x,int seed_y)
{
//结构体指针的声明
Im res,old;
res = new(Image); //记的初始化
old = new(Image);
//不使用静态变量会发生stack overflow的情况
static int **ima;
int i = 0;
int j = 0;
ima = zeros(1024,1024);
for (i = 0;i<1024;i++)
{
for (j=0;j<1024;j++)
res->I[i][j] = int(ima[i][j]);
}
old = copy(res,old);
res->I[seed_x][seed_y] = 1;
while (different(old->I,res->I))
{
old = copy(res,old);
int i = 1;
while (i < 1023)
{
int j = 1;
while (j < 1023)
{
if (res->I[i][j])
{
if (I[i - 1][j - 1]) res->I[i - 1][j - 1] = 1;
if (I[i - 1][j]) res->I[i - 1][j] = 1;
if (I[i - 1][j + 1]) res->I[i - 1][j + 1] = 1;
if (I[i][j - 1]) res->I[i][j - 1] = 1;
if (I[i][j + 1]) res->I[i][j + 1] = 1;
if (I[i + 1][j - 1]) res->I[i + 1][j - 1] = 1;
if (I[i + 1][j]) res->I[i + 1][j] = 1;
if (I[i + 1][j + 1]) res->I[i + 1][j + 1] = 1;
}
j++;
}
i++;
}
}
cout<<"Region Grow completed";
return res;
}
int ** zeros(int high,int width)
{
//通过指针声明二维数组
static int **res;
res = (int**)new int*[high];
for (int i=0;i< high;i++)
{
*(res+i) = new int[width];
}
for (int i = 0;i < high;i++)
{
for (int j = 0;j < width; j++)
{
res[i][j] = 0;
}
}
return res;
}
Im copy(Im sorce,Im target)
{
for (int i=0;i<1024;i++)
{
for (int j=0;j<1024;j++)
{
target->I[i][j] = sorce->I[i][j];
}
}
return target;
}
bool different(int f[1024][1024],int s[1024][1024])
{
for (int i = 0;i<1024;i++)
{
for (int j=0;j<1024;j++)
{
if (f[i][j] != s[i][j])
return true;
}
}
return false;
}
做完这些就可以编译dll了,编译运行完成后就可以用python调用它使用了。
3.python调用DLL库
在python的文当中这么进行如下编写
def R_G(I,seed_x,seed_y):
'''
The ALG of region_grow is based on binary images
:param I:
:param seed_x:
:param seed_y:
:return:
'''
in_put = ((c_int * 1024) * 1024)() #C++函数的输入类型,和C++的接口。
#把python的数据修改为C++可以接收的
for i in range(1024):
for j in range(1024):
in_put[i][j] = int(I[i, j])
#读取dll文件,可以写生成的dll地址
lib = ctypes.cdll.LoadLibrary('Region_Grow.dll')
h = lib.region_grow
h.restype = ctypes.POINTER(Ima)
p = lib.region_grow(in_put, seed_x, seed_y)
res = np.zeros([1024, 1024])
#将C++处理后的数据改变为python可以接受的类型
for i in range(1024):
for j in range(1024):
# print(p.contents.I[i][j])
res[i][j] = p.contents.I[i][j]
return res
#这两行的写法是和C++的结构体相对应
class Ima(ctypes.Structure):
_fields_ = [("I",(ctypes.c_int * 1024) * 1024)]
万事大吉,开始运行代码,端着茶继续抿一口,果然茶还没喝到就出结果了,用时大约1.5s,终于可以打出一口气了。其结果如下图所示。
写在最后:关于如何将C++的结构体和python结合起来,可以参考这个老哥的帖子