从《Python 图像风格迁移(模仿名画)》一文中看到 OpenCV 可以运行Torch 模型。也来试试,
先用vs2008编译一个cv3.3,
再下一个"fast-neural-style-master"包,按包中地址下载几个模型,下面是其中一个:
"http://cs.stanford.edu/people/jcjohns/fast-neural-style/models/instance_norm/candy.t7"
运行:
// 加载模型
dnn::Net net = cv::dnn::readNetFromTorch("candy.t7");
这里出错了,candy.t7中的部分Torch层在CV3.3不支持。
先来看一下模型的各层名称,和 fast-neural-style-tensorflow-master 比一比、有什么不同:
,
nn.Sequential
nn.SpatialReflectionPadding 补边
nn.SpatialConvolution 卷积 3-->16扩维
nn.InstanceNormalization 实例正则化
nn.SpatialBatchNormalization 批正则化
nn.ReLU relu激励
nn.SpatialConvolution 卷积 16-->32扩维,并2倍下采样
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialConvolution 卷积 32-->64扩维,并2倍下采样
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
--------------------------------残差块 1
nn.Sequential
nn.ConcatTable
nn.Sequential
nn.SpatialConvolution 卷积 64-->64维不变
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialConvolution 卷积 64-->64维不变
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ShaveImage 去边(边宽=补边宽/残差块数)
nn.CAddTable 矩阵相加
--------------------------------残差块 2
nn.Sequential
nn.ConcatTable
nn.Sequential
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ShaveImage
nn.CAddTable
--------------------------------残差块 3
nn.Sequential
nn.ConcatTable
nn.Sequential
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ShaveImage
nn.CAddTable
--------------------------------残差块 4
nn.Sequential
nn.ConcatTable
nn.Sequential
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ShaveImage
nn.CAddTable
--------------------------------残差块 5
nn.Sequential
nn.ConcatTable
nn.Sequential
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialConvolution
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ShaveImage
nn.CAddTable
nn.SpatialFullConvolution 全卷积 64-->32减维,并2倍上采样
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialFullConvolution 全卷积 32-->16减维,并2倍上采样
nn.InstanceNormalization
nn.SpatialBatchNormalization
nn.ReLU
nn.SpatialConvolution 卷积 16-->3减维
nn.Tanh
nn.MulConstant
nn.TotalVariation
相差不是很大,中间层维数是哪个tensorflow的一半,这个速度应该会快一点。
看来要编译CV4.0了,4.0对于vs2008已经没办法了,由于空间不足,就用mingw32编译吧
下了一个mingw32-4.92,费了一点波折,成功了
《Python OpenCV 图像风格迁移(模仿名画)》一文是、Python调用OpenCV, 这个暂时还不会,先搞一个C++的吧:
//核心代码: 加载模型 -> 读取图片 -> 前传计算 -> 输出图片
#include <opencv2\opencv.hpp>
#include <opencv2\dnn.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//----------------cv中取出 -----------------开始
const int CV_MAX_DIM = 32;
Mat getPlane(const Mat &m, int n, int cn)
{
CV_Assert(m.dims > 2);
int sz[CV_MAX_DIM];
for(int i = 2; i < m.dims; i++)
{
sz[i-2] = m.size.p[i];
}
return Mat(m.dims - 2, sz, m.type(), (void*)m.ptr<float>(n, cn));
}
//用于单图,如果多图还要再修改
void imagesFromBlob(const cv::Mat& blob_, OutputArrayOfArrays images_)
{
//blob 是浮点精度的4维矩阵
//blob_[0] = 批量大小 = 图像数
//blob_[1] = 通道数
//blob_[2] = 高度
//blob_[3] = 宽度
CV_Assert(blob_.depth() == CV_32F);
CV_Assert(blob_.dims == 4);
//images_.create(cv::Size(1, blob_.size[0]),blob_.depth() );//多图,不明白为什么?
images_.create(blob_.size[2],blob_.size[3],blob_.depth() );//创建一个图像
std::vector<Mat> vectorOfChannels(blob_.size[1]);
//for (int n = 0; n < blob_.size[0]; ++n) //多个图
{int n = 0; //只有一个图
for (int c = 0; c < blob_.size[1]; ++c)
{
vectorOfChannels[c] = getPlane(blob_, n, c);
}
//cv::merge(vectorOfChannels, images_.getMatRef(n));//这里会出错,是前面的create的原因?
cv::merge(vectorOfChannels, images_);//通道合并
}
}
//----------------cv中取出 -----------------结束
int main(int argc, char *argv[])
{
char jpgname[256];//图像名
if( argc == 2 )
strcpy_s(jpgname, argv[1]);
else
strcpy_s(jpgname, "5c6.jpg");
double time1 = static_cast<double>(getTickCount()); //记录起始时间
// 加载模型
dnn::Net net = cv::dnn::readNetFromTorch("mosaic.t7");
// 读取图片
Mat image = cv::imread(jpgname);
size_t h=image.rows;// 行数(高度)
size_t w=image.cols;// 列数(宽度)
cout<< w <<endl;
cout<< h <<endl;
Mat inputBlob;//转换为 (1,3,h,w) 的矩阵 即:(图像数,通道,高,宽)
//blobFromImage函数解释:
//第一个参数,InputArray image,表示输入的图像,可以是opencv的mat数据类型。
//第二个参数,scalefactor,这个参数很重要的,如果训练时,是归一化到0-1之间,那么这个参数就应该为0.00390625f (1/256),否则为1.0
//第三个参数,size,应该与训练时的输入图像尺寸保持一致。
//第四个参数,mean,这个主要在caffe中用到,caffe中经常会用到训练数据的均值。tf中貌似没有用到均值文件。
//第五个参数,swapRB,是否交换图像第1个通道和最后一个通道的顺序。
//第六个参数,crop,如果为true,就是裁剪图像,如果为false,就是等比例放缩图像。
//inputBlob= cv::dnn::blobFromImage(image, 1.0, Size(416, 416), Scalar(), false, true);//1/255.F
//inputBlob= cv::dnn::blobFromImage(image, 1.0, Size(416, 416*h/w), Scalar(103.939, 116.779, 123.680), false, true);//
inputBlob = cv::dnn::blobFromImage(image, 1.0, Size(w,h), Scalar(103.939, 116.779, 123.680), false, false);//
// 进行计算
net.setInput(inputBlob);
Mat out = net.forward();
Mat Styled;//4维转回3维
//cv::dnn::imagesFromBlob(out, Styled);//由于这个函数会出错,已把它从dnn取出稍稍修改一下用
imagesFromBlob(out, Styled);
// 输出图片
Styled /=255;
Mat uStyled;
Styled.convertTo(uStyled,CV_8U,255);//转换格式
cv::imshow("风格图像", uStyled);
cv::imwrite("风格图像.jpg", uStyled);
time1=((double)getTickCount()-time1)/getTickFrequency();//计算程序运行时间
cout<<"此方法运行时间为:"<<time1<<"秒"<<endl;//输出运行时间。
waitKey(0);
}
效果图:
这是mosaic风格的
和《fast-neural-style-master》一文比有点暗,应该是后处理有点不同吧,原文相当于做了一个归一化。