【機器學習】C++與OpenCV、Tensorflow-python聯合呼叫
上一篇我介紹了C++呼叫Python的入門方法。這一篇我講述C++與OpenCV、Tensorflow-python聯合呼叫的一次成功的實驗過程。
C++通過python呼叫tensorflow,比呼叫C++版本的tensorflow的優勢在於:tensorflow環境依賴python環境,python環境幾乎是一鍵傻瓜式操作,各種庫依賴的問題,比直接使用tensorflow-C++少得多,只要python配置好了,C++專案根本不需要單獨配置tensorflow,完全靠python模組。在C++專案配置中,根本不需要新增tensorflow的include標頭檔案目錄和庫目錄。
OpenCV是一個通用的計算機視覺庫,C++版本幾乎是傻瓜式編譯,最後只輸出opencv_world310d.lib / opencv_world310.lib庫檔案,和 opencv_world310d.dll / opencv_world310.dll動態連結庫,標頭檔案只需要包含#include <opencv2/opencv.hpp>
在C++專案中配置好python,numpy和OpenCV之後,就可以開始寫測試程式碼了:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <Python.h>
#include <numpy/arrayobject.h>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
int main(int argc, char *argv[])
{
Py_InitializeEx(1); //初始化 python
import_array();
if (!Py_IsInitialized())
{
cout << "initialized error" << endl;
return -1;
}
PyObject* sys = PyImport_ImportModule("sys");
std::cout << std::string(Py_GetVersion()) << "\n" ;
PyRun_SimpleString("import sys"); // 執行 python 中的短語句
PyRun_SimpleString("print('come in python')");
PyRun_SimpleString("sys.path.append('./')");
PyObject *pModule(0), *pDct(0);
pModule = PyImport_ImportModule("tfTest");
if (!pModule)
{
PyErr_Print();
cout << "can not find pytest.py" << endl;
return -1;
}
else
cout << "open Module" << endl;
cv::Mat oimg = cv::imread("camera.jpg", cv::IMREAD_UNCHANGED);
cv::Mat img;
cv::cvtColor(oimg, img, cv::COLOR_BGR2GRAY);//測試灰度圖
auto sz = img.size();
int x = sz.width;
int y = sz.height;
int z = img.channels();
int xy = x*y;
int channel = img.channels();
uchar *CArrays = new uchar[x*y*z];
int iChannels = img.channels();
int iRows = img.rows;
int iCols = img.cols * iChannels;
if (img.isContinuous())
{
iCols *= iRows;
iRows = 1;
}
uchar* p;
int id = -1;
for (int i = 0; i < iRows; i++)
{
// get the pointer to the ith row
p = img.ptr<uchar>(i);
// operates on each pixel
for (int j = 0; j < iCols; j++)
{
CArrays[++id] = p[j];//連續空間
}
}
npy_intp Dims[3] = { y, x, z}; //注意這個維度資料!
PyObject *pDict = PyModule_GetDict(pModule);
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, CArrays);
PyObject *PyPath = Py_BuildValue("s", "conv.jpg");
PyObject *ArgArray = PyTuple_New(2);
PyTuple_SetItem(ArgArray, 1, PyArray); //同樣定義大小與Python函式引數個數一致的PyTuple物件
PyTuple_SetItem(ArgArray, 0, PyPath); //同樣定義大小與Python函式引數個數一致的PyTuple物件
PyObject *pFunc = PyDict_GetItemString(pDict, "CVSaveImage");
PyObject_CallObject(pFunc, ArgArray);//呼叫函式,傳入Numpy Array 物件。
PyErr_Print();
Py_DECREF(ArgArray);
Py_DECREF(pDict);
Py_DECREF(sys);
Py_DECREF(pModule);
Py_Finalize();
delete[] CArrays;
return 0;
}
OpenCV讀取的影象是一個連續空間,可以直接賦值給numpy陣列。如果是彩色影象,那麼OpenCV Mat資料格式為一個[3*W,H]的一維陣列,其中每一行資料是按照R,G,B,R,G,B。。。來儲存,和OpenCV-python讀取影象後得到的numpy陣列格式是一致的,因此彩色影象也可以使用上述程式碼轉換為numpy資料。
python程式碼為:
#coding:utf-8
import tensorflow as tf
import numpy as np
import cv2
def CVSaveImage(path, img1):
img = img1.reshape([img1.shape[0],img1.shape[1],1,1])
img = img.transpose(3,0,1,2)
dia = 5
kernel = np.ones((dia,dia))/(dia**2.)
kernel = kernel.reshape([dia,dia,1,1])
sess = tf.Session()
A=tf.placeholder("float", shape=[1,img1.shape[0],img1.shape[1],1])
B=tf.placeholder("float", shape=[dia,dia,1,1])
initial = tf.random_normal([1,img1.shape[0],img1.shape[1],1]) * 0.256
X = tf.Variable(initial)
init = tf.global_variables_initializer()
sess.run(init)
X = tf.nn.conv2d(A,B,strides=[1,1,1,1],padding='SAME')
img2 = sess.run(X,feed_dict={A: img, B:kernel})
print(img2.shape)
img2= img2.transpose([1,2,3,0])
img2= img2.reshape([img1.shape[0],img1.shape[1]])
cv2.imwrite(path,img2)
這裡我預設使用了灰度圖來進行處理,channel通道始終是1。這個程式碼的功能是把影象進行一次均值濾波,得到結果:
至此,C++與OpenCV、Tensorflow-python聯合呼叫完成。
影象黑邊是我截圖的問題,原圖是沒有的。