C++调用Python脚本函数

C++调用Python脚本函数

文章目录

  • C++调用Python脚本函数
    • 1、编写python脚本,并下载安装所需的库文件(保证编写的python脚本在指定的虚拟环境下可以正常运行)
    • 2、在Vs2017中配置python运行环境
    • 3、代码编写(C++调用Python流程)

1、编写python脚本,并下载安装所需的库文件(保证编写的python脚本在指定的虚拟环境下可以正常运行)

        我这边在做项目的时候需要使用C++调用低照度图像增强的python程序,以下是我这边测试时使用的代码。

# coding:utf-8
from datetime import datetime
import numpy as np
import cv2
from math import ceil
from scipy.sparse import spdiags
from scipy.misc import imresize
from scipy.optimize import fminbound
from scipy.stats import entropy
from scipy.sparse.linalg import spsolve

imagePath = ""

def computeTextureWeights(fin, sigma, sharpness):
    # print(fin)
    # fin = fin / 255.0

    dt0_v = np.diff(fin, 1, 0)  # 垂直差分
    dt0_v = np.concatenate((dt0_v, fin[:1, :] - fin[-1:, :]), axis=0)  # 第0行减去最后一行

    dt0_h = np.diff(fin, 1, 1)  # 水平差分
    dt0_h = np.concatenate((dt0_h, fin[:, :1] - fin[:, -1:]), axis=1)  # 第0列减去最后一列

    gauker_h = cv2.filter2D(dt0_h, -1, np.ones((1, sigma)), borderType=cv2.BORDER_CONSTANT)
    gauker_v = cv2.filter2D(dt0_v, -1, np.ones((sigma, 1)), borderType=cv2.BORDER_CONSTANT)
    # cv2这个filter2D(镜像翻转)与MATLAB的filter2(补0)不同

    W_h = 1.0 / (abs(gauker_h) * abs(dt0_h) + sharpness)
    W_v = 1.0 / (abs(gauker_v) * abs(dt0_v) + sharpness)

    return W_h, W_v


def convertCol(tmp):  # 按照列转成列。[[1, 2, 3], [4, 5, 6], [7, 8, 9]] # 转成[147258369].T(竖着)
    return np.reshape(tmp.T, (tmp.shape[0] * tmp.shape[1], 1))


def solveLinearEquation(IN, wx, wy, lambd):
    # print('IN', IN.shape)
    r, c, ch = IN.shape[0], IN.shape[1], 1
    k = r * c
    dx = -lambd * convertCol(wx)  # 按列转成一列
    dy = -lambd * convertCol(wy)
    tempx = np.concatenate((wx[:, -1:], wx[:, 0:-1]), 1)  # 最后一列插入到第一列前面
    tempy = np.concatenate((wy[-1:, :], wy[0:-1, :]), 0)  # 最后一行插入到第一行前面
    dxa = -lambd * convertCol(tempx)
    dya = -lambd * convertCol(tempy)
    tempx = np.concatenate((wx[:, -1:], np.zeros((r, c - 1))), 1)  # 取wx最后一列放在第一列,其他为0
    tempy = np.concatenate((wy[-1:, :], np.zeros((r - 1, c))), 0)  # 取wy最后一行放在第一行,其他为0
    dxd1 = -lambd * convertCol(tempx)
    dyd1 = -lambd * convertCol(tempy)
    wx[:, -1:] = 0  # 最后一列置为0
    wy[-1:, :] = 0  # 最后一行置为0
    dxd2 = -lambd * convertCol(wx)
    dyd2 = -lambd * convertCol(wy)

    Ax = spdiags(np.concatenate((dxd1, dxd2), 1).T, np.array([-k + r, -r]), k, k)
    Ay = spdiags(np.concatenate((dyd1, dyd2), 1).T, np.array([-r + 1, -1]), k, k)
    # spdiags,与MATLAB不同,scipy是根据行来构造sp,而matlab是根据列来构造sp

    D = 1 - (dx + dy + dxa + dya)
    A = (Ax + Ay) + (Ax + Ay).T + spdiags(D.T, np.array([0]), k, k)

    A = A / 1000.0  # 需修改

    matCol = convertCol(IN)
    # print('spsolve开始', str(datetime.now()))
    OUT = spsolve(A, matCol, permc_spec="MMD_AT_PLUS_A")
    # print('spsolve结束', str(datetime.now()))
    OUT = OUT / 1000
    OUT = np.reshape(OUT, (c, r)).T
    return OUT


def tsmooth(I, lambd=0.5, sigma=5, sharpness=0.001):
    # print(I.shape)
    wx, wy = computeTextureWeights(I, sigma, sharpness)
    S = solveLinearEquation(I, wx, wy, lambd)
    return S


def rgb2gm(I):
    # print('I', I.shape)
    # I = np.maximum(I, 0.0)
    if I.shape[2] and I.shape[2] == 3:
        I = np.power(np.multiply(np.multiply(I[:, :, 0], I[:, :, 1]), I[:, :, 2]), (1.0 / 3))
    return I


def YisBad(Y, isBad):  # 此处需要修改得更高效
    return Y[isBad >= 1]
    # Z = []
    # [rows, cols] = Y.shape
    # for i in range(rows):
    #     for j in range(cols):
    #         if isBad[i, j] >= 122:
    #             Z.append(Y[i, j])
    # return np.array([Z]).T


def applyK(I, k, a=-0.3293, b=1.1258):
    # print(type(I))
    if not type(I) == 'numpy.ndarray':
        I = np.array(I)
    # print(type(I))
    beta = np.exp((1 - (k ** a)) * b)
    gamma = (k ** a)
    BTF = np.power(I, gamma) * beta
    # try:
    #    BTF = (I ** gamma) * beta
    # except:
    #    print('gamma:', gamma, '---beta:', beta)
    #    BTF = I
    return BTF


def maxEntropyEnhance(I, isBad, mink=1, maxk=10):
    # Y = rgb2gm(np.real(np.maximum(imresize(I, (50, 50), interp='bicubic') / 255.0, 0)))
    Y = imresize(I, (50, 50), interp='bicubic') / 255.0
    Y = rgb2gm(Y)
    # bicubic较为接近
    # Y = rgb2gm(np.real(np.maximum(cv2.resize(I, (50, 50), interpolation=cv2.INTER_LANCZOS4  ), 0)))
    # INTER_AREA 较为接近
    # import matplotlib.pyplot as plt
    # plt.imshow(Y, cmap='gray');plt.show()

    # print('isBad', isBad.shape)
    isBad = imresize(isBad.astype(int), (50, 50), interp='nearest')
    # print('isBad', isBad.shape)

    # plt.imshow(isBad, cmap='gray');plt.show()

    # 取出isBad为真的Y的值,形成一个列向量Y
    # Y = YisBad(Y, isBad)  # 此处需要修改得更高效
    Y = Y[isBad >= 1]

    # Y = sorted(Y)

    # print('-entropy(Y)', -entropy(Y))

    def f(k):
        return -entropy(applyK(Y, k))

    # opt_k = mink
    # k = mink
    # minF = f(k)
    # while k<= maxk:
    #     k+=0.0001
    #     if f(k)
    #         minF = f(k)
    #         opt_k = k
    opt_k = fminbound(f, mink, maxk)
    # print('opt_k:', opt_k)
    # opt_k = 5.363584
    # opt_k = 0.499993757705
    # optk有问题,fminbound正确,f正确,推测Y不一样导致不对
    # print('opt_k:', opt_k)
    J = applyK(I, opt_k) - 0.01
    return J


def HDR2dark(I, t_our, W):  # 过亮的地方变暗
    W = 1 - W
    I3 = I * W
    isBad = t_our > 0.8
    J3 = maxEntropyEnhance(I, isBad, 0.1, 0.5)  # 求k和曝光图
    J3 = J3 * (1 - W)  # 曝光图*权重
    fused = I3 + J3  # 增强图
    return I


def oneHDR(I, mu=0.5, a=-0.3293, b=1.1258):
    # mu照度图T的指数,数值越大,增强程度越大
    I = I / 255.0
    t_b = I[:, :, 0]  # t_b表示三通道图转成灰度图(灰度值为RGB中的最大值),亮度矩阵L
    for i in range(I.shape[2] - 1):  # 防止输入图片非三通道
        t_b = np.maximum(t_b, I[:, :, i + 1])
    # t_b2 = cv2.resize(t_b, (0, 0), fx=0.5, fy=0.5)
    # print('t_b', t_b.shape)
    # t_b2 = misc.imresize(t_b, (ceil(t_b.shape[0] / 2), ceil(t_b.shape[1] / 2)),interp='bicubic')
    # print('t_b2', t_b2.shape)
    # t_b2 = t_b / 255.0

    t_b2 = imresize(t_b, (256, 256), interp='bicubic', mode='F')  # / 255
    t_our = tsmooth(t_b2)  # 求解照度图T(灰度图)
    # print('t_our前', t_our.shape)
    t_our = imresize(t_our, t_b.shape, interp='bicubic', mode='F')  # / 255
    # print('t_our后', t_our.shape)

    # W: Weight Matrix 与 I2
    # 照度图L(灰度图) ->  照度图L(RGB图):灰度值重复3次赋给RGB
    # size为(I, 3) , 防止与原图尺寸有偏差
    t = np.ndarray(I.shape)
    for ii in range(I.shape[2]):
        t[:, :, ii] = t_our
    # print('t', t.shape)

    W = t ** mu  # 原图的权重。三维矩阵

    # cv2.imwrite(filepath + 'W.jpg', W * 255)
    # cv2.imwrite(filepath + '1-W.jpg', (1 - W) * 255)
    # cv2.imwrite(filepath + 't.jpg', t * 255)
    # cv2.imwrite(filepath + '1-t.jpg', (1 - t) * 255)

    # print('W', W.shape)
    # 变暗
    # isBad = t_our > 0.8  # 是高光照的像素点
    # I = maxEntropyEnhance(I, isBad)  # 求k和曝光图
    # 变暗
    I2 = I * W  # 原图*权重
   
   # 曝光率->k ->J
    isBad = t_our < 0.5  # 是低光照的像素点
    J = maxEntropyEnhance(I, isBad)  # 求k和曝光图
    J2 = J * (1 - W)  # 曝光图*权重
    fused = I2 + J2  # 增强图
    
    # 存储中间结果
    # cv2.imwrite(filepath + 'I2.jpg', I2 * 255.0)
    # cv2.imwrite(filepath + 'J2.jpg', J2 * 255.0)
    
    # 变暗
    # fused = HDR2dark(fused, t_our, W)
    return fused
    # return res

def test(imagePath):
    print('程序开始', str(datetime.now()))
    
    filepath = imagePath 
    inputImg = cv2.imread(filepath + 'Grey.jpg')
    outputImg = oneHDR(inputImg)
    
    # outputImg = outputImg * 255.0
    
    cv2.imwrite(filepath + '1out.jpg', outputImg * 255)
    
    # print("HDR完成,已保存到本地")
    print('程序结束', str(datetime.now()))

    # cv2.imshow('inputImg', inputImg)
    # cv2.imshow('outputImg', outputImg)
    # print(inputImg.dtype,outputImg.dtype)
    # outputImg = outputImg.astype(int)
    # print(inputImg.dtype, outputImg.dtype)
    # compare = np.concatenate((inputImg,outputImg),axis=1)
    # cv2.imshow('compare', compare)
    
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()
    
    flag = 1
    return flag

if __name__ == '__main__':
    # print('程序开始', str(datetime.now()))
    test('C:/Users/Administrator/Desktop/image/')

        代码来源:https://zhuanlan.zhihu.com/p/84450561
        需要的python环境:python3、numpy、opencv-pyhon、scipy,对应版本可参考如下:

C++调用Python脚本函数_第1张图片
        强烈推荐:使用豆瓣镜像安装python库文件(可解决下载慢,超时下载异常)

pip install (库文件名称) -i https://pypi.doubanio.com/simple

2、在Vs2017中配置python运行环境

        在Vs2017中配置python运行环境其实跟就配置Opencv运行环境类似,需要将Python安装路径下的include、libs和该路径下相关的Dll配置到Vs项目上即可。具体如下:

    1、打开python环境安装路径(如下图),获取include文件夹的路径信息,并将该路径信息复制到Vs项目->点击右键属性->VC++目录->包含目录下方。

C++调用Python脚本函数_第2张图片
    2、在当前项目工程下创建两个文件夹DLLs、libs(如下图1)。在python环境安装路径下的libs中找到python36.lib(如下图2),将其复制到新创建的libs文件夹中,且需将其再复制一份并重命名为python36_d.lib,同时放置在该libs文件夹下(如下图3);同理,在python环境安装路径下找到python36.dll(如下图4),将其复制到新创建的DLLs文件夹中,在将其再复制一份并重命名为python36_d.dll,放置在该DLLs文件夹下(如下图5)。

C++调用Python脚本函数_第3张图片
C++调用Python脚本函数_第4张图片
C++调用Python脚本函数_第5张图片
C++调用Python脚本函数_第6张图片
C++调用Python脚本函数_第7张图片
    3、点击项目右键属性->调试->环境下添加:
    PATH=$(SolutionDir)C++调用Python接口实现图像增强\DLLs;%PATH%(添加DLl路径信息)

    点击项目右键属性->VC++ 目录->库目录下添加:
    $(SolutionDir)C++调用Python接口实现图像增强\libs(添加libs路径信息)

    点击项目右键属性->链接器->输入->附加依赖项下添加:
    python36_d.lib、python36.lib

3、代码编写(C++调用Python流程)

        C++调用Python流程如下:
        1、首先需要包含头文件
        2、初始化Python环境 Py_Initialize()
        3、添加脚本路径 PyRun_SimpleString()
        4、导入模块 PyImport_ImportModule()
        5、导入函数 PyObject_GetAttrString()
        6、导入参数 Py_BuildValue()、PyEval_CallObject()
        7、获取返回值 PyArg_Parse()
        8、结束释放 Py_Finalize()

        代码如下:

#include 
#include 

int main()
{
	wchar_t wt[] = L"E:\\Anaconda2\\envs\\python3\\envs\\enhance";
	Py_SetPythonHome(wt);

	//初始化Python环境
	Py_Initialize();
	
	//添加模块路径
	PyRun_SimpleString("import sys");
	//当前目录下
	PyRun_SimpleString("sys.path.append('./')");
    
	//导入模块
	PyObject *pModule = PyImport_ImportModule("enhance");
	if (!pModule)
	{
		std::cout << "Python模块导入失败" << std::endl;
		return 0;
	}
	std::cout << "Python模块导入成功" << std::endl;

	//导入函数
	PyObject *pFun = PyObject_GetAttrString(pModule, "test");
	if (!pFun)
	{
		std::cout << "Python函数导入失败" << std::endl;
		return 0;
	}
	std::cout << "Python函数导入成功" << std::endl;

	//导入参数
	PyObject *args = Py_BuildValue("(s)", "C:/Users/Administrator/Desktop/image/");
	PyObject *pArg = PyEval_CallObject(pFun, args);  //PyObject_CallObject(pFun, args)都行
	if (!pArg)
	{
		std::cout << "Python函数参数导入失败" << std::endl;
		return 0;
	}
	std::cout << "Python函数参数导入成功" << std::endl;

	//获取函数返回值
	int res = 0;
	PyArg_Parse(pArg, "i", &res);
	std::cout << res << std::endl;

	//结束释放
	Py_Finalize();

	return 1;
}

        在此、说一下我在编写程序的过程中遇到的问题:

        1、初始化Python环境 Py_Initialize()时,运行程序报错(如下图):原因是因为电脑上存在多个虚拟环境,要指定到所需的虚拟环境路径才行。因此要在初始化之前加上Py_SetPythonHome(wt),wt为指定虚拟环境的路径信息。

C++调用Python脚本函数_第8张图片
        2、在导入模块 PyImport_ImportModule()时,总是返回Null:原因是因为PyRun_SimpleString(“sys.path.append(‘./’)”);该语句指定Python脚本应位与当前项目工程下,而我的Python脚本放置在桌面上,导致无法加载成功。需将Python脚本放置在当前项目工程下,或将sys.path.append()中的路径改为桌面路径即可。我这边直接将Python脚本放在当前工程下,已解决。

C++调用Python脚本函数_第9张图片
        3、在导入参数Py_BuildValue()时,参考如下列表导入参数(暂时就知道这么多,往后用到了新的我会及时更新):

命令 实际输入
Py_BuildValue(“”) None
Py_BuildValue(“(i)”, 123) 123
Py_BuildValue(“(i,i)”, 123, 456) (123, 456)
Py_BuildValue(“(s)”, “hello”) ‘hello’

        效果展示:

C++调用Python脚本函数_第10张图片

C++调用Python脚本函数_第11张图片

C++调用Python脚本函数_第12张图片

你可能感兴趣的:(Opencv专栏,python,c++,开发语言)