在写《瘦脸实现【Python】》时候实现了在python环境下的瘦脸功能,但实际使用起来,python的处理速度实在太慢,让人难以接受,所以用c语言重新实现了一次,速度有了很大的提升。因为之前的UI是用python搭建的,所以想通过python调用c的方法实现算法的加速。
因为联想到opencv也有python的API,在python下,图像处理的速度也很快,所以就查找了opencv通过怎样的方法来实现numpy转Mat的过程。
首先搜到了,这篇博客,但是经过实验发现该方法并不支持python3.x版本(后来在原作者GitHub上经过提问,作者给出了最新版本,支持python2和3),后来我就自己翻看opencv的源码(XXX/modules/python/src2/cv2.cpp)发现numpy转化为Mat跟 pyopencv_to函数有关,于是我就找到了《从PyOpenCV到CV2》这篇博文,结合该代码和源码,将瘦脸代码改写成c代码,并编译成库,提供给python调用。
ps:在查找资料的过程中,也有大神是通过boost库实现的,但我觉得为了转换还多依赖一个库,很麻烦,而且opencv好像也并没有依赖该库,所以并没有按照这个方法去实验。
下面对实现的过程做一个详细的记录。
环境:【windows7+vs2013+python3.5】 和 【 windows10+vs2017+python3.6】
1.【建立Dll动态库工程】
建立一个dll工程,并添加如下头文件,库文件目录。python是64位系统的,平台要选择x64。
库目录,库文件
然后写瘦脸的c代码:
#include <Python.h>
#include<malloc.h>
#include <numpy/arrayobject.h>
typedef unsigned char uchar;
//最近邻插值法
uchar* BilinearInter(uchar* data, int step_length, float ux, float uy)
{
int ux_i = round(ux);
int uy_i = round(uy);
return (data + uy_i * step_length + ux_i * 3);
}
uchar * localTransform(uchar* data, int channel, int width, int height, int startX, int startY, int endX, int endY, float radius)
{
float ddradius = radius * radius;
int data_size = channel * width*height;
int step_length = channel * width;
uchar* copyImg = (uchar*)malloc(sizeof(uchar) *data_size);
memcpy(copyImg, data, data_size);
float ddmc = (endX - startX) * (endX - startX) + (endY - startY) * (endY - startY);
for (int j = 0; j < height; j++)
{
uchar* rowData = copyImg + j * step_length;
for (int i = 0; i < width; i++)
{
if (abs(i - startX) > radius && abs(j - startY) > radius)
continue;
float distance = (i - startX) * (i - startX) + (j - startY) * (j - startY);
if (distance < ddradius)
{
float ratio = (ddradius - distance) / (ddradius - distance + ddmc);
ratio = ratio * ratio;
float UX = i - ratio * (endX - startX);
float UY = j - ratio * (endY - startY);
uchar *insertValue = BilinearInter(data, step_length, UX, UY);
memcpy(rowData + i * channel, insertValue, channel);
}
}
}
memcpy(data, copyImg, data_size);
free(copyImg);
return data;
}
static int failmsg(const char *fmt, ...) {
char str[1000];
va_list ap;
va_start(ap, fmt);
vsnprintf(str, sizeof(str), fmt, ap);
va_end(ap);
PyErr_SetString(PyExc_TypeError, str);
return 0;
}
//尝试查看传过来的数组的数据
static PyObject* face_data(PyObject* self, PyObject* args)
{
PyObject* pyobj_img = NULL;
char ok = PyArg_ParseTuple(args, "O", &pyobj_img);
if (ok)
{
PyArrayObject* oarr = (PyArrayObject*)pyobj_img;
int typenum = PyArray_TYPE(oarr), new_typenum = typenum;
int ndims = PyArray_NDIM(oarr);
uchar* data = (uchar*)PyArray_DATA(oarr);
localTransform(data, 3, 606, 738, 145, 487, 447, 473, 80.0);
PyObject *result = PyUnicode_FromFormat("result:%s", "ok");
return result;
}
else
{
PyObject *result = PyUnicode_FromFormat("result:%d", 10);
return result;
}
}
static PyObject *
face_hello(PyObject *self, PyObject *args) {
PyObject *name, *result;
if (!PyArg_ParseTuple(args, "U:demo_hello", &name))
return NULL;
result = PyUnicode_FromFormat("Hello, %S!", name);
return result;
}
// method table
static PyMethodDef DemoMethods[] = {
{ "hello", face_hello, METH_VARARGS, "I guess here is description." },
{ "face_data", (PyCFunction)face_data, METH_VARARGS | METH_KEYWORDS, "I guess here is description." },
{ NULL, NULL, 0, NULL } /* Sentinel */
};
// The method table must be referenced in the module definition structure.
static struct PyModuleDef demomodule = {
PyModuleDef_HEAD_INIT,
"facethin", /* name of module */
NULL, /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module,
or -1 if the module keeps state in global variables. */
DemoMethods
};
// The initialization function must be named PyInit_name()
PyMODINIT_FUNC
PyInit_demo(void)
{
return PyModule_Create(&demomodule);
}
最后生成动态库facethin.dll,修改后缀名demo.pyd,然后将demo.pyd移动到C:\Users\aUsernme\AppData\Local\Programs\Python\Python36\DLLs,其中username计算机用户名。
编写python代码:
import demo
import cv2
import time
img=cv2.imread('timg4.jpg')
start = time.clock()
hi=demo.demo_data(img)
end = time.clock()
print('run time:',end-start)
cv2.imshow('img',img)
cv2.imwrite('thin.jpg',img)
cv2.waitKey(0)
运行效果:
原图
瘦脸图:
总结:
在我的机器上,时间上大概是20多ms,而纯python实现需要1.5s左右,提升非常明显。
本文的主要目的是介绍下如何将numpy转c指针,瘦脸中某些参数只针对实例图像有效,不适用其他图像。