設計が破綻していたので全面的に書きなおした

This commit is contained in:
lltcggie 2016-07-03 13:37:26 +09:00
parent 2b9781f4af
commit 9fa4d905fd
12 changed files with 2423 additions and 1989 deletions

63
.gitattributes vendored Normal file
View File

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

245
.gitignore vendored Normal file
View File

@ -0,0 +1,245 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Xx]64/
[Xx]86/
[Bb]uild/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Un-comment the next line if you do not want to checkin
# your web deploy settings because they may include unencrypted
# passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
AppPackages/
BundleArtifacts/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# LightSwitch generated files
GeneratedArtifacts/
ModelManifest.xml
# Paket dependency manager
.paket/paket.exe
# FAKE - F# Make
.fake/

710
common/cNet.cpp Normal file
View File

@ -0,0 +1,710 @@
#include "cNet.h"
#include <caffe/caffe.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <rapidjson/document.h>
#include <opencv2/imgproc.hpp>
const int kProtoReadBytesLimit = INT_MAX; // Max size of 2 GB minus 1 byte.
template<typename BufType>
static bool readFile(boost::iostreams::stream<boost::iostreams::file_descriptor_source> &is, std::vector<BufType> &buf)
{
if (!is)
return false;
const auto size = is.seekg(0, std::ios::end).tellg();
is.seekg(0, std::ios::beg);
buf.resize((size / sizeof(BufType)) + (size % sizeof(BufType)));
is.read(buf.data(), size);
if (is.gcount() != size)
return false;
return true;
}
template<typename BufType>
static bool readFile(const boost::filesystem::path &path, std::vector<BufType> &buf)
{
boost::iostreams::stream<boost::iostreams::file_descriptor_source> is;
try
{
is.open(path, std::ios_base::in | std::ios_base::binary);
}
catch (...)
{
return false;
}
return readFile(is, buf);
}
static Waifu2x::eWaifu2xError readProtoText(const boost::filesystem::path &path, ::google::protobuf::Message* proto)
{
boost::iostreams::stream<boost::iostreams::file_descriptor_source> is;
try
{
is.open(path, std::ios_base::in);
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
}
if (!is)
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
std::vector<char> tmp;
if (!readFile(is, tmp))
return Waifu2x::eWaifu2xError_FailedParseModelFile;
google::protobuf::io::ArrayInputStream input(tmp.data(), tmp.size());
const bool success = google::protobuf::TextFormat::Parse(&input, proto);
if (!success)
return Waifu2x::eWaifu2xError_FailedParseModelFile;
return Waifu2x::eWaifu2xError_OK;
}
static Waifu2x::eWaifu2xError writeProtoBinary(const ::google::protobuf::Message& proto, const boost::filesystem::path &path)
{
boost::iostreams::stream<boost::iostreams::file_descriptor> os;
try
{
os.open(path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
}
if (!os)
return Waifu2x::eWaifu2xError_FailedWriteModelFile;
if (!proto.SerializePartialToOstream(&os))
return Waifu2x::eWaifu2xError_FailedWriteModelFile;
return Waifu2x::eWaifu2xError_OK;
}
static Waifu2x::eWaifu2xError readProtoBinary(const boost::filesystem::path &path, ::google::protobuf::Message* proto)
{
boost::iostreams::stream<boost::iostreams::file_descriptor_source> is;
try
{
is.open(path, std::ios_base::in | std::ios_base::binary);
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
}
if (!is)
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
std::vector<char> tmp;
if (!readFile(is, tmp))
return Waifu2x::eWaifu2xError_FailedParseModelFile;
google::protobuf::io::ArrayInputStream input(tmp.data(), tmp.size());
google::protobuf::io::CodedInputStream coded_input(&input);
coded_input.SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);
const bool success = proto->ParseFromCodedStream(&coded_input);
if (!success)
return Waifu2x::eWaifu2xError_FailedParseModelFile;
return Waifu2x::eWaifu2xError_OK;
}
cNet::cNet() : mModelScale(0), mInnerScale(0), mNetOffset(0), mInputPlane(0)
{}
cNet::~cNet()
{}
// モデルファイルからネットワークを構築
// processでcudnnが指定されなかった場合はcuDNNが呼び出されないように変更する
Waifu2x::eWaifu2xError cNet::ConstractNet(const boost::filesystem::path &model_path, const boost::filesystem::path &param_path, const boost::filesystem::path &info_path, const std::string &process)
{
Waifu2x::eWaifu2xError ret;
ret = LoadInfoFromJson(info_path);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
boost::filesystem::path modelbin_path = model_path;
modelbin_path += ".protobin";
boost::filesystem::path caffemodel_path = param_path;
caffemodel_path += ".caffemodel";
caffe::NetParameter param_model;
caffe::NetParameter param_caffemodel;
const auto retModelBin = readProtoBinary(modelbin_path, &param_model);
const auto retParamBin = readProtoBinary(caffemodel_path, &param_caffemodel);
if (retModelBin == Waifu2x::eWaifu2xError_OK && retParamBin == Waifu2x::eWaifu2xError_OK)
{
ret = SetParameter(param_model, process);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
if (!caffe::UpgradeNetAsNeeded(caffemodel_path.string(), &param_caffemodel))
return Waifu2x::eWaifu2xError_FailedParseModelFile;
mNet = boost::shared_ptr<caffe::Net<float>>(new caffe::Net<float>(param_model));
mNet->CopyTrainedLayersFrom(param_caffemodel);
}
else
{
const auto ret = LoadParameterFromJson(model_path, param_path, modelbin_path, caffemodel_path, process);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
}
const auto &inputs = mNet->input_blobs();
if (inputs.empty())
return Waifu2x::eWaifu2xError_FailedConstructModel;
if (mInputPlane != inputs[0]->channels())
return Waifu2x::eWaifu2xError_FailedConstructModel;
return Waifu2x::eWaifu2xError_OK;
}
Waifu2x::eWaifu2xError cNet::LoadInfoFromJson(const boost::filesystem::path &info_path)
{
rapidjson::Document d;
std::vector<char> jsonBuf;
try
{
boost::iostreams::stream<boost::iostreams::file_descriptor_source> is;
try
{
is.open(info_path, std::ios_base::in | std::ios_base::binary);
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
}
if (!is)
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
const size_t size = is.seekg(0, std::ios::end).tellg();
is.seekg(0, std::ios::beg);
jsonBuf.resize(size + 1);
is.read(jsonBuf.data(), jsonBuf.size());
jsonBuf[jsonBuf.size() - 1] = '\0';
d.Parse(jsonBuf.data());
const bool resize = d.HasMember("resize") && d["resize"].GetBool() ? true : false;
const auto name = d["name"].GetString();
const int channels = d["channels"].GetInt();
const int net_offset = d["offset"].GetInt();
const int inner_scale = d["scale_factor"].GetInt();
mModelScale = 2; // TODO: 動的に設定するようにする
mInnerScale = inner_scale;
mNetOffset = net_offset;
mInputPlane = channels;
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedParseModelFile;
}
return Waifu2x::eWaifu2xError_OK;
}
Waifu2x::eWaifu2xError cNet::SetParameter(caffe::NetParameter &param, const std::string &process) const
{
param.mutable_state()->set_phase(caffe::TEST);
{
auto input_layer = param.mutable_layer(0);
auto mid = input_layer->mutable_input_param()->mutable_shape();
if (mid->size() != 1 || mid->Mutable(0)->dim_size() != 4)
return Waifu2x::eWaifu2xError_FailedParseModelFile;
mid->Mutable(0)->set_dim(0, 1);
mid->Mutable(0)->set_dim(2, 142);
mid->Mutable(0)->set_dim(3, 142);
}
for (int i = 0; i < param.layer_size(); i++)
{
caffe::LayerParameter *layer_param = param.mutable_layer(i);
const std::string& type = layer_param->type();
if (type == "Convolution")
{
if (process == "cudnn")
layer_param->mutable_convolution_param()->set_engine(caffe::ConvolutionParameter_Engine_CUDNN);
else
layer_param->mutable_convolution_param()->set_engine(caffe::ConvolutionParameter_Engine_CAFFE);
}
else if (type == "ReLU")
{
if (process == "cudnn")
layer_param->mutable_relu_param()->set_engine(caffe::ReLUParameter_Engine_CUDNN);
else
layer_param->mutable_relu_param()->set_engine(caffe::ReLUParameter_Engine_CAFFE);
}
}
return Waifu2x::eWaifu2xError_OK;
}
Waifu2x::eWaifu2xError cNet::LoadParameterFromJson(const boost::filesystem::path &model_path, const boost::filesystem::path &param_path
, const boost::filesystem::path &modelbin_path, const boost::filesystem::path &caffemodel_path, const std::string &process)
{
Waifu2x::eWaifu2xError ret;
caffe::NetParameter param;
ret = readProtoText(model_path, &param);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
ret = writeProtoBinary(param, modelbin_path);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
ret = SetParameter(param, process);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
mNet = boost::shared_ptr<caffe::Net<float>>(new caffe::Net<float>(param));
rapidjson::Document d;
std::vector<char> jsonBuf;
try
{
boost::iostreams::stream<boost::iostreams::file_descriptor_source> is;
try
{
is.open(param_path, std::ios_base::in | std::ios_base::binary);
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
}
if (!is)
return Waifu2x::eWaifu2xError_FailedOpenModelFile;
const size_t size = is.seekg(0, std::ios::end).tellg();
is.seekg(0, std::ios::beg);
jsonBuf.resize(size + 1);
is.read(jsonBuf.data(), jsonBuf.size());
jsonBuf[jsonBuf.size() - 1] = '\0';
d.Parse(jsonBuf.data());
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedParseModelFile;
}
if (d.Size() != 7)
return Waifu2x::eWaifu2xError_FailedParseModelFile;
int inputPlane = 0;
int outputPlane = 0;
try
{
inputPlane = d[0]["nInputPlane"].GetInt();
outputPlane = d[d.Size() - 1]["nOutputPlane"].GetInt();
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedParseModelFile;
}
if (inputPlane == 0 || outputPlane == 0)
return Waifu2x::eWaifu2xError_FailedParseModelFile;
if (inputPlane != outputPlane)
return Waifu2x::eWaifu2xError_FailedParseModelFile;
//if (param.layer_size() < 17)
// return Waifu2x::eWaifu2xError_FailedParseModelFile;
std::vector<boost::shared_ptr<caffe::Layer<float>>> list;
auto &v = mNet->layers();
for (auto &l : v)
{
auto lk = l->type();
auto &bv = l->blobs();
if (bv.size() > 0)
list.push_back(l);
}
try
{
std::vector<float> weightList;
std::vector<float> biasList;
int count = 0;
for (auto it = d.Begin(); it != d.End(); ++it)
{
const auto &weight = (*it)["weight"];
const auto nInputPlane = (*it)["nInputPlane"].GetInt();
const auto nOutputPlane = (*it)["nOutputPlane"].GetInt();
const auto kW = (*it)["kW"].GetInt();
const auto &bias = (*it)["bias"];
auto leyer = list[count];
auto &b0 = leyer->blobs()[0];
auto &b1 = leyer->blobs()[1];
float *b0Ptr = nullptr;
float *b1Ptr = nullptr;
if (caffe::Caffe::mode() == caffe::Caffe::CPU)
{
b0Ptr = b0->mutable_cpu_data();
b1Ptr = b1->mutable_cpu_data();
}
else
{
b0Ptr = b0->mutable_gpu_data();
b1Ptr = b1->mutable_gpu_data();
}
const auto WeightSize1 = weight.Size();
const auto WeightSize2 = weight[0].Size();
const auto KernelHeight = weight[0][0].Size();
const auto KernelWidth = weight[0][0][0].Size();
if (!(b0->count() == WeightSize1 * WeightSize2 * KernelHeight * KernelWidth))
return Waifu2x::eWaifu2xError_FailedConstructModel;
if (!(b1->count() == bias.Size()))
return Waifu2x::eWaifu2xError_FailedConstructModel;
weightList.resize(0);
biasList.resize(0);
size_t weightCount = 0;
for (auto it2 = weight.Begin(); it2 != weight.End(); ++it2)
{
for (auto it3 = (*it2).Begin(); it3 != (*it2).End(); ++it3)
{
for (auto it4 = (*it3).Begin(); it4 != (*it3).End(); ++it4)
{
for (auto it5 = (*it4).Begin(); it5 != (*it4).End(); ++it5)
weightList.push_back((float)it5->GetDouble());
}
}
}
caffe::caffe_copy(b0->count(), weightList.data(), b0Ptr);
for (auto it2 = bias.Begin(); it2 != bias.End(); ++it2)
biasList.push_back((float)it2->GetDouble());
caffe::caffe_copy(b1->count(), biasList.data(), b1Ptr);
count++;
}
mNet->ToProto(&param);
ret = writeProtoBinary(param, caffemodel_path);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedConstructModel;
}
return Waifu2x::eWaifu2xError_OK;
}
int cNet::GetInputPlane() const
{
return mInputPlane;
}
int cNet::GetInnerScale() const
{
return mInnerScale;
}
int cNet::GetNetOffset() const
{
return mNetOffset;
}
int cNet::GetScale() const
{
return mModelScale;
}
int cNet::GetInputMemorySize(const int crop_w, const int crop_h, const int outer_padding, const int batch_size) const
{
const int InputPadding = mNetOffset + outer_padding;
const auto input_block_width = crop_w + InputPadding * 2;
const auto input_block_height = crop_h + InputPadding * 2;
const int input_block_plane_size = input_block_width * input_block_height * mInputPlane;
return input_block_plane_size * sizeof(float);
}
int cNet::GetOutputMemorySize(const int crop_w, const int crop_h, const int outer_padding, const int batch_size) const
{
const int InputPadding = mNetOffset + outer_padding;
const auto input_block_width = crop_w + InputPadding * 2;
const auto input_block_height = crop_h + InputPadding * 2;
const auto output_block_width = input_block_width * mInnerScale - mNetOffset * 2;
const auto output_block_height = input_block_height * mInnerScale - mNetOffset * 2;
const int output_block_plane_size = output_block_width * output_block_height * mInputPlane;
return output_block_plane_size * sizeof(float);
}
// ネットワークを使って画像を再構築する
Waifu2x::eWaifu2xError cNet::ReconstructImage(const bool UseTTA, const int crop_w, const int crop_h, const int outer_padding, const int batch_size, float *inputBlockBuf, float *outputBlockBuf, const cv::Mat &inMat, cv::Mat &outMat)
{
const auto InputHeight = inMat.size().height;
const auto InputWidth = inMat.size().width;
const auto InputLine = inMat.step1();
assert(inMat.channels() == 1 || inMat.channels() == 3);
const int InputPadding = mNetOffset + outer_padding; // 入力パディング
const auto NoPaddingInputWidth = InputWidth - InputPadding * 2; // パディングを除いた入力画像サイズ(横)
const auto NoPaddingInputHeight = InputHeight - InputPadding * 2; // パディングを除いた入力画像サイズ(縦)
cv::Mat outim(NoPaddingInputHeight * mInnerScale, NoPaddingInputWidth * mInnerScale, inMat.type());
// float *imptr = (float *)im.data;
float *imptr = (float *)outim.data;
const auto input_block_width = crop_w + InputPadding * 2; // 入力ブロックサイズ(横)
const auto input_block_height = crop_h + InputPadding * 2; // 入力ブロックサイズ(縦)
const auto output_block_width = input_block_width * mInnerScale - mNetOffset * 2; // 出力ブロックサイズ(横)
const auto output_block_height = input_block_height * mInnerScale - mNetOffset * 2; // 出力ブロックサイズ(縦)
const auto output_crop_block_width = crop_w * mInnerScale; // クロップ後の出力ブロックサイズ(横)
const auto output_crop_block_height = crop_h * mInnerScale; // クロップ後の出力ブロックサイズ(縦)
const auto output_crop_w = (output_block_width - crop_w * mInnerScale) / 2; // 出力後のクロップサイズ
const auto output_crop_h = (output_block_height - crop_h * mInnerScale) / 2; // 出力後のクロップサイズ
assert(NoPaddingInputWidth % crop_w == 0);
assert(NoPaddingInputHeight % crop_h == 0);
try
{
auto input_blobs = mNet->input_blobs();
assert(input_blobs.size() > 0);
auto input_blob = mNet->input_blobs()[0];
input_blob->Reshape(batch_size, mInputPlane, input_block_height, input_block_width);
assert(inMat.channels() == mInputPlane);
assert(input_blob->shape(1) == mInputPlane);
const int WidthNum = NoPaddingInputWidth / crop_w;
const int HeightNum = NoPaddingInputHeight / crop_h;
const int BlockNum = WidthNum * HeightNum;
const int input_block_plane_size = input_block_width * input_block_height * mInputPlane;
const int output_block_plane_size = output_block_width * output_block_height * mInputPlane;
// 画像は(消費メモリの都合上)block_size*block_sizeに分けて再構築する
for (int num = 0; num < BlockNum; num += batch_size)
{
const int processNum = (BlockNum - num) >= batch_size ? batch_size : BlockNum - num;
if (processNum < batch_size)
input_blob->Reshape(processNum, mInputPlane, input_block_height, input_block_width);
for (int n = 0; n < processNum; n++)
{
const int wn = (num + n) % WidthNum;
const int hn = (num + n) / WidthNum;
const int w = wn * crop_w;
const int h = hn * crop_h;
assert(w + input_block_width <= InputWidth && h + input_block_height <= InputHeight);
cv::Mat someimg = inMat(cv::Rect(w, h, input_block_width, input_block_height));
// 画像を直列に変換
{
float *fptr = inputBlockBuf + (input_block_plane_size * n);
const float *uptr = (const float *)someimg.data;
const auto Line = someimg.step1();
if (someimg.channels() == 1)
{
if (input_block_width == Line)
memcpy(fptr, uptr, input_block_width * input_block_height * sizeof(float));
else
{
for (int i = 0; i < input_block_height; i++)
memcpy(fptr + i * input_block_width, uptr + i * Line, input_block_width * sizeof(float));
}
}
else
{
const auto LinePixel = someimg.step1() / someimg.channels();
const auto Channel = someimg.channels();
const auto Width = someimg.size().width;
const auto Height = someimg.size().height;
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
{
for (int ch = 0; ch < Channel; ch++)
{
const size_t IndexSrc = i * someimg.step1() + j * Channel + ch;
const size_t IndexDst = (ch * Height + i) * Width + j;
fptr[IndexDst] = uptr[IndexSrc];
}
}
}
}
}
}
assert(input_blob->count() == input_block_plane_size * processNum);
// ネットワークに画像を入力
input_blob->set_cpu_data(inputBlockBuf);
// 計算
auto out = mNet->Forward();
auto b = out[0];
assert(b->count() == output_block_plane_size * processNum);
const float *ptr = nullptr;
if (caffe::Caffe::mode() == caffe::Caffe::CPU)
ptr = b->cpu_data();
else
ptr = b->gpu_data();
caffe::caffe_copy(output_block_plane_size * processNum, ptr, outputBlockBuf);
for (int n = 0; n < processNum; n++)
{
const int wn = (num + n) % WidthNum;
const int hn = (num + n) / WidthNum;
const int w = wn * output_crop_block_width;
const int h = hn * output_crop_block_height;
const float *fptr = outputBlockBuf + (output_block_plane_size * n);
// 結果を出力画像にコピー
if (outim.channels() == 1)
{
for (int i = 0; i < output_crop_block_height; i++)
memcpy(imptr + (h + i) * InputLine + w, fptr + (i + output_crop_h) * output_block_width + output_crop_w, output_crop_block_width * sizeof(float));
}
else
{
const auto LinePixel = outim.step1() / outim.channels();
const auto Channel = outim.channels();
//for (int i = 0; i < output_no_padding_block_height; i++)
//{
// for (int j = 0; j < output_no_padding_block_width; j++)
// {
// for (int ch = 0; ch < Channel; ch++)
// imptr[((h + i) * LinePixel + (w + j)) * Channel + ch]
// = fptr[(ch * output_block_height + i + output_crop_h) * output_block_width + j + output_padding];
// }
//}
for (int i = 0; i < output_crop_block_height; i++)
{
for (int j = 0; j < output_crop_block_width; j++)
{
for (int ch = 0; ch < Channel; ch++)
{
const size_t IndexSrc = (ch * output_block_height + i + output_crop_h) * output_block_width + j + output_crop_w;
const size_t IndexDst = ((h + i) * LinePixel + (w + j)) * Channel + ch;
imptr[IndexDst] = fptr[IndexSrc];
}
}
}
}
//{
// cv::Mat testim(output_block_size, output_block_size, CV_32FC1);
// float *p = (float *)testim.data;
// for (int i = 0; i < output_block_size; i++)
// {
// for (int j = 0; j < output_block_size; j++)
// {
// p[testim.step1() * i + j] = fptr[i * output_block_size + j];
// }
// }
// const int cv_depth = DepthBitToCVDepth(8);
// const double max_val = GetValumeMaxFromCVDepth(cv_depth);
// const double eps = GetEPS(cv_depth);
// cv::Mat write_iamge;
// testim.convertTo(write_iamge, cv_depth, max_val, eps);
// cv::imwrite("ti.png", write_iamge);
// testim.release();
//}
}
}
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedProcessCaffe;
}
// 値を01にクリッピング
cv::threshold(outim, outim, 1.0, 1.0, cv::THRESH_TRUNC);
cv::threshold(outim, outim, 0.0, 0.0, cv::THRESH_TOZERO);
outMat = outim;
return Waifu2x::eWaifu2xError_OK;
}

37
common/cNet.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include "waifu2x.h"
class cNet
{
private:
boost::shared_ptr<caffe::Net<float>> mNet;
int mModelScale; // モデルが対象とする拡大率
int mInnerScale; // ネット内部で拡大される倍率
int mNetOffset; // ネットに入力するとどれくらい削れるか
int mInputPlane; // ネットへの入力チャンネル数
private:
Waifu2x::eWaifu2xError LoadParameterFromJson(const boost::filesystem::path &model_path, const boost::filesystem::path &param_path
, const boost::filesystem::path &modelbin_path, const boost::filesystem::path &caffemodel_path, const std::string &process);
Waifu2x::eWaifu2xError LoadInfoFromJson(const boost::filesystem::path &info_path);
Waifu2x::eWaifu2xError SetParameter(caffe::NetParameter &param, const std::string &process) const;
public:
cNet();
~cNet();
Waifu2x::eWaifu2xError ConstractNet(const boost::filesystem::path &model_path, const boost::filesystem::path &param_path, const boost::filesystem::path &info_path, const std::string &process);
int GetInputPlane() const;
int GetInnerScale() const;
int GetNetOffset() const;
int GetScale() const;
int GetInputMemorySize(const int crop_w, const int crop_h, const int outer_padding, const int batch_size) const;
int GetOutputMemorySize(const int crop_w, const int crop_h, const int outer_padding, const int batch_size) const;
Waifu2x::eWaifu2xError ReconstructImage(const bool UseTTA, const int crop_w, const int crop_h, const int outer_padding, const int batch_size, float *inputBlockBuf, float *outputBlockBuf, const cv::Mat &inMat, cv::Mat &outMat);
};

873
common/stImage.cpp Normal file
View File

@ -0,0 +1,873 @@
#include "stImage.h"
#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/algorithm/string.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
const int YToRGBConvertMode = CV_GRAY2RGB;
const int YToRGBConverInversetMode = CV_RGB2GRAY;
const int BGRToYConvertMode = CV_BGR2YUV;
const int BGRToConvertInverseMode = CV_YUV2BGR;
// floatな画像をuint8_tな画像に変換する際の四捨五入に使う値
// https://github.com/nagadomi/waifu2x/commit/797b45ae23665a1c5e3c481c018e48e6f0d0e383
const double clip_eps8 = (1.0 / 255.0) * 0.5 - (1.0e-7 * (1.0 / 255.0) * 0.5);
const double clip_eps16 = (1.0 / 65535.0) * 0.5 - (1.0e-7 * (1.0 / 65535.0) * 0.5);
const double clip_eps32 = 1.0 * 0.5 - (1.0e-7 * 0.5);
const std::vector<stImage::stOutputExtentionElement> stImage::OutputExtentionList =
{
{L".png",{8, 16}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".bmp",{8}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".jpg",{8}, 0, 100, 95, cv::IMWRITE_JPEG_QUALITY},
{L".jp2",{8, 16}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".sr",{8}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".tif",{8, 16, 32}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".hdr",{8, 16, 32}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".exr",{8, 16, 32}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".ppm",{8, 16}, boost::optional<int>(), boost::optional<int>(), boost::optional<int>(), boost::optional<int>()},
{L".webp",{8}, 1, 100, 100, cv::IMWRITE_WEBP_QUALITY},
{L".tga",{8}, 0, 1, 0, 0},
};
template<typename BufType>
static bool readFile(boost::iostreams::stream<boost::iostreams::file_descriptor_source> &is, std::vector<BufType> &buf)
{
if (!is)
return false;
const auto size = is.seekg(0, std::ios::end).tellg();
is.seekg(0, std::ios::beg);
buf.resize((size / sizeof(BufType)) + (size % sizeof(BufType)));
is.read(buf.data(), size);
if (is.gcount() != size)
return false;
return true;
}
template<typename BufType>
static bool readFile(const boost::filesystem::path &path, std::vector<BufType> &buf)
{
boost::iostreams::stream<boost::iostreams::file_descriptor_source> is;
try
{
is.open(path, std::ios_base::in | std::ios_base::binary);
}
catch (...)
{
return false;
}
return readFile(is, buf);
}
template<typename BufType>
static bool writeFile(boost::iostreams::stream<boost::iostreams::file_descriptor> &os, const std::vector<BufType> &buf)
{
if (!os)
return false;
const auto WriteSize = sizeof(BufType) * buf.size();
os.write((const char *)buf.data(), WriteSize);
if (os.fail())
return false;
return true;
}
template<typename BufType>
static bool writeFile(const boost::filesystem::path &path, std::vector<BufType> &buf)
{
boost::iostreams::stream<boost::iostreams::file_descriptor> os;
try
{
os.open(path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
}
catch (...)
{
return false;
}
return writeFile(os, buf);
}
static void Waifu2x_stbi_write_func(void *context, void *data, int size)
{
boost::iostreams::stream<boost::iostreams::file_descriptor> *osp = (boost::iostreams::stream<boost::iostreams::file_descriptor> *)context;
osp->write((const char *)data, size);
}
int stImage::DepthBitToCVDepth(const int depth_bit)
{
switch (depth_bit)
{
case 8:
return CV_8U;
case 16:
return CV_16U;
case 32:
return CV_32F;
}
// 不明だけどとりあえずCV_8Uを返しておく
return CV_8U;
}
double stImage::GetValumeMaxFromCVDepth(const int cv_depth)
{
switch (cv_depth)
{
case CV_8U:
return 255.0;
case CV_16U:
return 65535.0;
case CV_32F:
return 1.0;
}
// 不明だけどとりあえず255.0を返しておく
return 255.0;
}
double stImage::GetEPS(const int cv_depth)
{
switch (cv_depth)
{
case CV_8U:
return clip_eps8;
case CV_16U:
return clip_eps16;
case CV_32F:
return clip_eps32;
}
// 不明だけどとりあえずclip_eps8返しておく
return clip_eps8;
}
Waifu2x::eWaifu2xError stImage::AlphaMakeBorder(std::vector<cv::Mat> &planes, const cv::Mat &alpha, const int offset)
{
// このカーネルと画像の畳込みを行うと、(x, y)を中心とした3×3領域の合計値が求まる
const static cv::Mat sum2d_kernel = (cv::Mat_<double>(3, 3) <<
1., 1., 1.,
1., 1., 1.,
1., 1., 1.);
cv::Mat mask;
cv::threshold(alpha, mask, 0.0, 1.0, cv::THRESH_BINARY); // アルファチャンネルを二値化してマスクとして扱う
cv::Mat mask_nega;
cv::threshold(mask, mask_nega, 0.0, 1.0, cv::THRESH_BINARY_INV); // 反転したマスク値が1の箇所は完全透明でない有効な画素となる
for (auto &p : planes) // 完全に透明なピクセルにあるゴミを取る
{
p = p.mul(mask);
}
for (int i = 0; i < offset; i++)
{
cv::Mat mask_weight;
cv::filter2D(mask, mask_weight, -1, sum2d_kernel, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT); // マスクの3×3領域の合計値を求める
cv::Mat mask_nega_u8;
mask_nega.convertTo(mask_nega_u8, CV_8U, 255.0, clip_eps8); // mask_negaのCV_U8版OpenCVのAPI上必要になる
for (auto &p : planes) // 1チャンネルずつ処理
{
// チャンネルの3×3領域内の有効画素の平均値を求める
cv::Mat border;
cv::filter2D(p, border, -1, sum2d_kernel, cv::Point(-1, -1), 0, cv::BORDER_DEFAULT);
border /= mask_weight;
// チャンネルの有効な画素の部分に、計算した平均値をコピー
border.copyTo(p, mask_nega_u8);
}
// マスクを1回膨張させたものを新しいマスクとする(マスクの3×3領域の合計値を求めたものの非0領域は、マスクを1回膨張させたものの領域に等しい)
cv::threshold(mask_weight, mask, 0.0, 1.0, cv::THRESH_BINARY);
// 新しいマスクの反転したマスクを計算
cv::threshold(mask, mask_nega, 0.0, 1.0, cv::THRESH_BINARY_INV);
}
// 画素を0から1にクリッピング
for (auto &p : planes)
{
cv::threshold(p, p, 1.0, 1.0, cv::THRESH_TRUNC);
cv::threshold(p, p, 0.0, 0.0, cv::THRESH_TOZERO);
}
return Waifu2x::eWaifu2xError_OK;
}
// 画像を読み込んで値を0.0f1.0fの範囲に変換
Waifu2x::eWaifu2xError stImage::LoadMat(cv::Mat &im, const boost::filesystem::path &input_file)
{
cv::Mat original_image;
{
std::vector<char> img_data;
if (!readFile(input_file, img_data))
return Waifu2x::eWaifu2xError_FailedOpenInputFile;
cv::Mat im(img_data.size(), 1, CV_8U, img_data.data());
original_image = cv::imdecode(im, cv::IMREAD_UNCHANGED);
if (original_image.empty())
{
const Waifu2x::eWaifu2xError ret = LoadMatBySTBI(original_image, img_data);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
}
}
im = original_image;
return Waifu2x::eWaifu2xError_OK;
}
Waifu2x::eWaifu2xError stImage::LoadMatBySTBI(cv::Mat &im, const std::vector<char> &img_data)
{
int x, y, comp;
stbi_uc *data = stbi_load_from_memory((const stbi_uc *)img_data.data(), img_data.size(), &x, &y, &comp, 0);
if (!data)
return Waifu2x::eWaifu2xError_FailedOpenInputFile;
int type = 0;
switch (comp)
{
case 1:
case 3:
case 4:
type = CV_MAKETYPE(CV_8U, comp);
break;
default:
return Waifu2x::eWaifu2xError_FailedOpenInputFile;
}
im = cv::Mat(cv::Size(x, y), type);
const auto LinePixel = im.step1() / im.channels();
const auto Channel = im.channels();
const auto Width = im.size().width;
const auto Height = im.size().height;
assert(x == Width);
assert(y == Height);
assert(Channel == comp);
auto ptr = im.data;
for (int i = 0; i < y; i++)
{
for (int j = 0; j < x; j++)
{
for (int ch = 0; ch < Channel; ch++)
ptr[(i * LinePixel + j) * comp + ch] = data[(i * x + j) * comp + ch];
}
}
stbi_image_free(data);
if (comp >= 3)
{
// RGBだからBGRに変換
for (int i = 0; i < y; i++)
{
for (int j = 0; j < x; j++)
std::swap(ptr[(i * LinePixel + j) * comp + 0], ptr[(i * LinePixel + j) * comp + 2]);
}
}
return Waifu2x::eWaifu2xError_OK;
}
cv::Mat stImage::ConvertToFloat(const cv::Mat &im)
{
cv::Mat convert;
switch (im.depth())
{
case CV_8U:
im.convertTo(convert, CV_32F, 1.0 / GetValumeMaxFromCVDepth(CV_8U));
break;
case CV_16U:
im.convertTo(convert, CV_32F, 1.0 / GetValumeMaxFromCVDepth(CV_16U));
break;
case CV_32F:
convert = im; // 元から0.01.0のはずなので変換は必要ない
break;
}
return convert;
}
stImage::stImage() : mIsRequestDenoise(false), pad_w1(0), pad_h1(0), pad_w2(0), pad_h2(0)
{
}
stImage::~stImage()
{
}
void stImage::Clear()
{
mOrgFloatImage.release();
mTmpImageRGB.release();
mTmpImageA.release();
mEndImage.release();
}
Waifu2x::eWaifu2xError stImage::Load(const boost::filesystem::path &input_file)
{
Clear();
Waifu2x::eWaifu2xError ret;
cv::Mat im;
ret = LoadMat(im, input_file);
if (ret != Waifu2x::eWaifu2xError_OK)
return ret;
mOrgFloatImage = im;
mOrgChannel = im.channels();
mOrgSize = im.size();
const boost::filesystem::path ip(input_file);
const boost::filesystem::path ipext(ip.extension());
const bool isJpeg = boost::iequals(ipext.string(), ".jpg") || boost::iequals(ipext.string(), ".jpeg");
mIsRequestDenoise = isJpeg;
return Waifu2x::eWaifu2xError_OK;
}
Waifu2x::eWaifu2xError stImage::Load(const void* source, const int width, const int height, const int channel, const int stride)
{
cv::Mat original_image(cv::Size(width, height), CV_MAKETYPE(CV_8U, channel), (void *)source, stride);
if (original_image.channels() >= 3) // RGBなのでBGRにする
{
std::vector<cv::Mat> planes;
cv::split(original_image, planes);
std::swap(planes[0], planes[2]);
cv::merge(planes, original_image);
}
mOrgFloatImage = original_image;
return Waifu2x::eWaifu2xError_OK;
}
bool stImage::RequestDenoise() const
{
return mIsRequestDenoise;
}
void stImage::Preprocess(const int input_plane, const int net_offset)
{
mOrgFloatImage = ConvertToFloat(mOrgFloatImage);
ConvertToNetFormat(input_plane, net_offset);
}
void stImage::ConvertToNetFormat(const int input_plane, const int alpha_offset)
{
if (input_plane == 1) // Yモデル
{
if (mOrgFloatImage.channels() == 1) // 1chだけなのでそのまま
mTmpImageRGB = mOrgFloatImage;
else // BGRなので変換
{
mTmpImageRGB = mOrgFloatImage;
if (mTmpImageRGB.channels() == 4) // BGRAなのでAだけ取り出す
{
std::vector<cv::Mat> planes;
cv::split(mTmpImageRGB, planes);
mTmpImageA = planes[3];
planes.resize(3);
AlphaMakeBorder(planes, mTmpImageA, alpha_offset); // 透明なピクセルと不透明なピクセルの境界部分の色を広げる
cv::merge(planes, mTmpImageRGB);
}
CreateBrightnessImage(mTmpImageRGB, mTmpImageRGB);
}
}
else // RGBモデル
{
if (mOrgFloatImage.channels() == 1) // 1chだけなのでRGBに変換
{
cv::cvtColor(mOrgFloatImage, mTmpImageRGB, YToRGBConvertMode);
mOrgFloatImage.release();
}
else // BGRからRGBに変換(AがあったらAも取り出す)
{
std::vector<cv::Mat> planes;
cv::split(mOrgFloatImage, planes);
mOrgFloatImage.release();
if (planes.size() == 4) // BGRAなのでAだけ取り出す
{
mTmpImageA = planes[3];
planes.resize(3);
AlphaMakeBorder(planes, mTmpImageA, alpha_offset); // 透明なピクセルと不透明なピクセルの境界部分の色を広げる
// α拡大用にRGBに変換
cv::cvtColor(mTmpImageA, mTmpImageA, CV_GRAY2RGB);
}
// BGRからRGBにする
std::swap(planes[0], planes[2]);
cv::merge(planes, mTmpImageRGB);
}
mOrgFloatImage.release();
}
}
// 画像から輝度の画像を取り出す
Waifu2x::eWaifu2xError stImage::CreateBrightnessImage(const cv::Mat &float_image, cv::Mat &im)
{
if (float_image.channels() > 1)
{
cv::Mat converted_color;
cv::cvtColor(float_image, converted_color, BGRToYConvertMode);
std::vector<cv::Mat> planes;
cv::split(converted_color, planes);
im = planes[0];
planes.clear();
}
else
im = float_image;
return Waifu2x::eWaifu2xError_OK;
}
bool stImage::HasAlpha() const
{
return !mTmpImageA.empty();
}
void stImage::GetScalePaddingedRGB(cv::Mat &im, cv::Size_<int> &size, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, const int scale)
{
GetScalePaddingedImage(mTmpImageRGB, im, size, net_offset, outer_padding, crop_w, crop_h, scale);
}
void stImage::SetReconstructedRGB(cv::Mat &im, const cv::Size_<int> &size, const int inner_scale)
{
SetReconstructedImage(mTmpImageRGB, im, size, inner_scale);
}
void stImage::GetScalePaddingedA(cv::Mat &im, cv::Size_<int> &size, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, const int scale)
{
GetScalePaddingedImage(mTmpImageA, im, size, net_offset, outer_padding, crop_w, crop_h, scale);
}
void stImage::SetReconstructedA(cv::Mat &im, const cv::Size_<int> &size, const int inner_scale)
{
SetReconstructedImage(mTmpImageA, im, size, inner_scale);
}
void stImage::GetScalePaddingedImage(cv::Mat &in, cv::Mat &out, cv::Size_<int> &size, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, const int scale)
{
cv::Mat ret;
if (scale > 1)
{
cv::Size_<int> zoom_size = in.size();
zoom_size.width *= scale;
zoom_size.height *= scale;
cv::resize(in, ret, zoom_size, 0.0, 0.0, cv::INTER_NEAREST);
}
else
ret = in;
in.release();
size = ret.size();
PaddingImage(ret, net_offset, outer_padding, crop_w, crop_h, ret);
out = ret;
}
// 入力画像の(Photoshopでいう)キャンバスサイズをoutput_sizeの倍数に変更
// 画像は左上配置、余白はcv::BORDER_REPLICATEで埋める
void stImage::PaddingImage(const cv::Mat &input, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, cv::Mat &output)
{
const auto pad_w1 = net_offset + outer_padding;
const auto pad_h1 = net_offset + outer_padding;
const auto pad_w2 = (int)ceil((double)input.size().width / (double)crop_w) * crop_w - input.size().width + net_offset + outer_padding;
const auto pad_h2 = (int)ceil((double)input.size().height / (double)crop_h) * crop_h - input.size().height + net_offset + outer_padding;
cv::copyMakeBorder(input, output, pad_h1, pad_h2, pad_w1, pad_w2, cv::BORDER_REPLICATE);
}
// 拡大、パディングされた画像を設定
void stImage::SetReconstructedImage(cv::Mat &dst, cv::Mat &src, const cv::Size_<int> &size, const int inner_scale)
{
const cv::Size_<int> s(size * inner_scale);
// ブロックサイズ用のパディングを取り払う(outer_paddingは再構築の過程で取り除かれている)
dst = src(cv::Rect(0, 0, s.width, s.height));
src.release();
}
void stImage::Postprocess(const int input_plane, const double scale, const int depth)
{
DeconvertFromNetFormat(input_plane);
ShrinkImage(scale);
// 値を01にクリッピング
cv::threshold(mEndImage, mEndImage, 1.0, 1.0, cv::THRESH_TRUNC);
cv::threshold(mEndImage, mEndImage, 0.0, 0.0, cv::THRESH_TOZERO);
mEndImage = DeconvertFromFloat(mEndImage, depth);
AlphaCleanImage(mEndImage);
}
void stImage::DeconvertFromNetFormat(const int input_plane)
{
if (input_plane == 1) // Yモデル
{
if (mOrgChannel == 1) // もともと1chだけなのでそのまま
{
mEndImage = mTmpImageRGB;
mTmpImageRGB.release();
mOrgFloatImage.release();
}
else // もともとBGRなので既存アルゴリズムで拡大したUVに拡大したYを合体して戻す
{
std::vector<cv::Mat> color_planes;
CreateZoomColorImage(mOrgFloatImage, mTmpImageRGB.size(), color_planes);
mOrgFloatImage.release();
color_planes[0] = mTmpImageRGB;
mTmpImageRGB.release();
cv::Mat converted_image;
cv::merge(color_planes, converted_image);
color_planes.clear();
cv::cvtColor(converted_image, mEndImage, BGRToConvertInverseMode);
converted_image.release();
}
}
else // RGBモデル
{
// ここの地点でmOrgFloatImageは空
if (mOrgChannel == 1) // もともと1chだけなので戻す
{
cv::cvtColor(mTmpImageRGB, mEndImage, YToRGBConverInversetMode);
mTmpImageRGB.release();
}
else // もともとBGRなのでRGBから戻す(AがあったらAも合体して戻す)
{
std::vector<cv::Mat> planes;
cv::split(mTmpImageRGB, planes);
mTmpImageRGB.release();
if (!mTmpImageA.empty()) // Aもあるので合体
{
// RGBから1chに戻す
cv::cvtColor(mTmpImageA, mTmpImageA, CV_RGB2GRAY);
planes.push_back(mTmpImageA);
mTmpImageA.release();
}
// RGBからBGRにする
std::swap(planes[0], planes[2]);
cv::merge(planes, mEndImage);
}
}
}
void stImage::ShrinkImage(const double scale)
{
// TODO: scale = 1.0 でも悪影響を及ぼさないか調べる
const int scaleBase = 2; // TODO: モデルの拡大率によって可変できるようにする
const int scaleNum = ceil(log(scale) / log(scaleBase));
const double shrinkRatio = scale >= 1.0 ? scale / std::pow(scaleBase, scaleNum) : scale;
const cv::Size_<int> ns(mOrgSize.width * scale, mOrgSize.height * scale);
//if (mEndImage.size().width != ns.width || mEndImage.size().height != ns.height)
//{
// int argo = cv::INTER_CUBIC;
// if (scale < 0.5)
// argo = cv::INTER_AREA;
// cv::resize(mEndImage, mEndImage, ns, 0.0, 0.0, argo);
//}
}
cv::Mat stImage::DeconvertFromFloat(const cv::Mat &im, const int depth)
{
const int cv_depth = DepthBitToCVDepth(depth);
const double max_val = GetValumeMaxFromCVDepth(cv_depth);
const double eps = GetEPS(cv_depth);
cv::Mat ret;
if (depth == 32) // 出力がfloat形式なら変換しない
ret = im;
else
im.convertTo(ret, cv_depth, max_val, eps);
return ret;
}
namespace
{
template<typename T>
void AlphaZeroToZero(std::vector<cv::Mat> &planes)
{
cv::Mat alpha(planes[3]);
const T *aptr = (const T *)alpha.data;
T *ptr0 = (T *)planes[0].data;
T *ptr1 = (T *)planes[1].data;
T *ptr2 = (T *)planes[2].data;
const size_t Line = alpha.step1();
const size_t Width = alpha.size().width;
const size_t Height = alpha.size().height;
for (size_t i = 0; i < Height; i++)
{
for (size_t j = 0; j < Width; j++)
{
const size_t pos = Line * i + j;
if (aptr[pos] == (T)0)
ptr0[pos] = ptr1[pos] = ptr2[pos] = (T)0;
}
}
}
}
void stImage::AlphaCleanImage(cv::Mat &im)
{
// 完全透明のピクセルの色を消す(処理の都合上、完全透明のピクセルにも色を付けたから)
// モデルによっては画像全域の完全透明の場所にごく小さい値のアルファが広がることがある。それを消すためにcv_depthへ変換してからこの処理を行うことにした
// (ただしcv_depthが32の場合だと意味は無いが)
// TODO: モデル(例えばPhoto)によっては0しかない画像を変換しても0.000114856390とかになるので、適切な値のクリッピングを行う?
if (im.channels() > 3)
{
std::vector<cv::Mat> planes;
cv::split(im, planes);
im.release();
const auto depth = planes[0].depth();
switch (depth)
{
case CV_8U:
AlphaZeroToZero<uint8_t>(planes);
break;
case CV_16U:
AlphaZeroToZero<uint16_t>(planes);
break;
case CV_32F:
AlphaZeroToZero<float>(planes);
break;
case CV_64F:
AlphaZeroToZero<double>(planes);
break;
}
cv::merge(planes, im);
}
}
// 入力画像をzoom_sizeの大きさにcv::INTER_CUBICで拡大し、色情報のみを残す
Waifu2x::eWaifu2xError stImage::CreateZoomColorImage(const cv::Mat &float_image, const cv::Size_<int> &zoom_size, std::vector<cv::Mat> &cubic_planes)
{
cv::Mat zoom_cubic_image;
cv::resize(float_image, zoom_cubic_image, zoom_size, 0.0, 0.0, cv::INTER_CUBIC);
cv::Mat converted_cubic_image;
cv::cvtColor(zoom_cubic_image, converted_cubic_image, BGRToYConvertMode);
zoom_cubic_image.release();
cv::split(converted_cubic_image, cubic_planes);
converted_cubic_image.release();
// このY成分は使わないので解放
cubic_planes[0].release();
return Waifu2x::eWaifu2xError_OK;
}
cv::Mat stImage::GetEndImage() const
{
return mEndImage;
}
Waifu2x::eWaifu2xError stImage::Save(const boost::filesystem::path &output_file, const boost::optional<int> &output_quality)
{
return WriteMat(mEndImage, output_file, output_quality);
}
Waifu2x::eWaifu2xError stImage::WriteMat(const cv::Mat &im, const boost::filesystem::path &output_file, const boost::optional<int> &output_quality)
{
const boost::filesystem::path ip(output_file);
const std::string ext = ip.extension().string();
if (boost::iequals(ext, ".tga"))
{
unsigned char *data = im.data;
std::vector<unsigned char> rgbimg;
if (im.channels() >= 3 || im.step1() != im.size().width * im.channels()) // RGB用バッファにコピー(あるいはパディングをとる)
{
const auto Line = im.step1();
const auto Channel = im.channels();
const auto Width = im.size().width;
const auto Height = im.size().height;
rgbimg.resize(Width * Height * Channel);
const auto Stride = Width * Channel;
for (int i = 0; i < Height; i++)
memcpy(rgbimg.data() + Stride * i, im.data + Line * i, Stride);
data = rgbimg.data();
}
if (im.channels() >= 3) // BGRをRGBに並び替え
{
const auto Line = im.step1();
const auto Channel = im.channels();
const auto Width = im.size().width;
const auto Height = im.size().height;
auto ptr = rgbimg.data();
for (int i = 0; i < Height; i++)
{
for (int j = 0; j < Width; j++)
std::swap(ptr[(i * Width + j) * Channel + 0], ptr[(i * Width + j) * Channel + 2]);
}
}
boost::iostreams::stream<boost::iostreams::file_descriptor> os;
try
{
os.open(output_file, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
}
catch (...)
{
return Waifu2x::eWaifu2xError_FailedOpenOutputFile;
}
if (!os)
return Waifu2x::eWaifu2xError_FailedOpenOutputFile;
// RLE圧縮の設定
bool isSet = false;
const auto &OutputExtentionList = stImage::OutputExtentionList;
for (const auto &elm : OutputExtentionList)
{
if (elm.ext == L".tga")
{
if (elm.imageQualitySettingVolume && output_quality)
{
stbi_write_tga_with_rle = *output_quality;
isSet = true;
}
break;
}
}
// 設定されなかったのでデフォルトにする
if (!isSet)
stbi_write_tga_with_rle = 1;
if (!stbi_write_tga_to_func(Waifu2x_stbi_write_func, &os, im.size().width, im.size().height, im.channels(), data))
return Waifu2x::eWaifu2xError_FailedOpenOutputFile;
return Waifu2x::eWaifu2xError_OK;
}
try
{
const boost::filesystem::path op(output_file);
const boost::filesystem::path opext(op.extension());
std::vector<int> params;
const auto &OutputExtentionList = stImage::OutputExtentionList;
for (const auto &elm : OutputExtentionList)
{
if (elm.ext == opext)
{
if (elm.imageQualitySettingVolume && output_quality)
{
params.push_back(*elm.imageQualitySettingVolume);
params.push_back(*output_quality);
}
break;
}
}
std::vector<uchar> buf;
cv::imencode(ext, im, buf, params);
if (writeFile(output_file, buf))
return Waifu2x::eWaifu2xError_OK;
}
catch (...)
{
}
return Waifu2x::eWaifu2xError_FailedOpenOutputFile;
}

125
common/stImage.h Normal file
View File

@ -0,0 +1,125 @@
#pragma once
#include "waifu2x.h"
class stImage
{
private:
cv::Mat mOrgFloatImage;
int mOrgChannel;
cv::Size_<int> mOrgSize;
bool mIsRequestDenoise;
cv::Mat mTmpImageRGB; // RGB(あるいはY)
cv::Mat mTmpImageA; // αチャンネル
cv::Mat mEndImage; // 完成した画像
int pad_w1;
int pad_h1;
int pad_w2;
int pad_h2;
public:
struct stOutputExtentionElement
{
std::wstring ext;
std::vector<int> depthList;
boost::optional<int> imageQualityStart;
boost::optional<int> imageQualityEnd;
boost::optional<int> imageQualityDefault;
boost::optional<int> imageQualitySettingVolume;
};
const static std::vector<stOutputExtentionElement> OutputExtentionList;
private:
static Waifu2x::eWaifu2xError LoadMatBySTBI(cv::Mat &im, const std::vector<char> &img_data);
static cv::Mat ConvertToFloat(const cv::Mat &im);
static Waifu2x::eWaifu2xError AlphaMakeBorder(std::vector<cv::Mat> &planes, const cv::Mat &alpha, const int offset);
static cv::Mat DeconvertFromFloat(const cv::Mat &im, const int depth);
static void AlphaCleanImage(cv::Mat &im);
static Waifu2x::eWaifu2xError WriteMat(const cv::Mat &im, const boost::filesystem::path &output_file, const boost::optional<int> &output_quality);
void ConvertToNetFormat(const int input_plane, const int alpha_offset);
Waifu2x::eWaifu2xError CreateBrightnessImage(const cv::Mat &float_image, cv::Mat &im);
void PaddingImage(const cv::Mat &input, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, cv::Mat &output);
Waifu2x::eWaifu2xError CreateZoomColorImage(const cv::Mat &float_image, const cv::Size_<int> &zoom_size, std::vector<cv::Mat> &cubic_planes);
// 拡大、パディングされた画像を取得
// この関数を呼び出した後、mTmpImageRGBは空になるので注意
void GetScalePaddingedImage(cv::Mat &in, cv::Mat &out, cv::Size_<int> &size, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, const int scale);
// 変換された画像を設定
// size: GetScalePaddingedImage()で取得したsize
void SetReconstructedImage(cv::Mat &dst, cv::Mat &src, const cv::Size_<int> &size, const int inner_scale);
void DeconvertFromNetFormat(const int input_plane);
void ShrinkImage(const double scale);
static int DepthBitToCVDepth(const int depth_bit);
static double GetValumeMaxFromCVDepth(const int cv_depth);
static double GetEPS(const int cv_depth);
public:
stImage();
~stImage();
void Clear();
static Waifu2x::eWaifu2xError LoadMat(cv::Mat &im, const boost::filesystem::path &input_file);
Waifu2x::eWaifu2xError Load(const boost::filesystem::path &input_file);
// source: (4チャンネルの場合は)RGBAな画素配列
// dest: (4チャンネルの場合は)処理したRGBAな画素配列
// width: widthの縦幅
// height: heightの横幅
// channel: sourceのチャンネル数
// stride: sourceのストライド(バイト単位)
// sourceはPostprocess()が終わるまで存在している必要がある
Waifu2x::eWaifu2xError Load(const void* source, const int width, const int height, const int channel, const int stride);
bool RequestDenoise() const;
// 前処理
// RGBモデルの場合はこれが終わった時にはmOrgFloatImageが空になっているので注意
void Preprocess(const int input_plane, const int net_offset);
bool HasAlpha() const;
// 拡大、パディングされた画像を取得
// この関数を呼び出した後、mTmpImageRGBは空になるので注意
void GetScalePaddingedRGB(cv::Mat &im, cv::Size_<int> &size, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, const int scale);
// 変換された画像を設定
// この関数を呼び出した後、imは空になるので注意
// size: GetScalePaddingedImage()で取得したsize
void SetReconstructedRGB(cv::Mat &im, const cv::Size_<int> &size, const int inner_scale);
// 拡大、パディングされた画像を取得
// この関数を呼び出した後、mTmpImageAは空になるので注意
void GetScalePaddingedA(cv::Mat &im, cv::Size_<int> &size, const int net_offset, const int outer_padding,
const int crop_w, const int crop_h, const int scale);
// 拡大された画像を設定
// この関数を呼び出した後、imは空になるので注意
// size: GetScalePaddingedImage()で取得したsize
void SetReconstructedA(cv::Mat &im, const cv::Size_<int> &size, const int inner_scale);
void Postprocess(const int input_plane, const double scale, const int depth);
cv::Mat GetEndImage() const;
Waifu2x::eWaifu2xError Save(const boost::filesystem::path &output_file, const boost::optional<int> &output_quality);
};

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,10 @@ namespace caffe
class NetParameter;
};
class cNet;
class stImage;
class Waifu2x
{
public:
@ -59,130 +63,72 @@ public:
typedef std::function<bool()> waifu2xCancelFunc;
struct stOutputExtentionElement
{
std::wstring ext;
std::vector<int> depthList;
boost::optional<int> imageQualityStart;
boost::optional<int> imageQualityEnd;
boost::optional<int> imageQualityDefault;
boost::optional<int> imageQualitySettingVolume;
};
const static std::vector<stOutputExtentionElement> OutputExtentionList;
static std::string ExeDir;
private:
bool is_inited;
bool mIsInited;
// 一度に処理する画像の幅
int crop_size;
// 一度に何ブロック分処理するか
int batch_size;
std::string mMode;
int mNoiseLevel;
std::string mProcess;
// ネットに入力する画像のサイズ
int input_block_size;
// ネットに入力するブロックのサイズ(パディングなし)
int block_size;
// ネットワークに入力する画像のサイズ(出力画像の幅はlayer_num * 2だけ小さくなる)
int block_width_height;
// srcnn.prototxtで定義された入力する画像のサイズ
int original_width_height;
bool mIsCuda;
std::string mode;
int noise_level;
boost::optional<double> scale_ratio;
boost::optional<int> scale_width;
boost::optional<int> scale_height;
boost::filesystem::path model_dir;
std::string process;
std::shared_ptr<cNet> mNoiseNet;
std::shared_ptr<cNet> mScaleNet;
int inner_padding;
int outer_padding;
int mInputPlane; // ネットへの入力チャンネル数
int mMaxNetOffset; // ネットに入力するとどれくらい削れるか
int output_block_size;
float *mInputBlock;
size_t mInputBlockSize;
int input_plane;
bool isCuda;
boost::shared_ptr<caffe::Net<float>> net_noise;
boost::shared_ptr<caffe::Net<float>> net_scale;
float *input_block;
float *dummy_data;
float *output_block;
bool use_tta;
boost::optional<int> output_quality;
int output_depth;
int model_scale; // モデルが対象とする拡大率
int net_offset; // ネットに入力するとどれくらい削れるか
int inner_scale; // ネットで拡大される倍率
float *mOutputBlock;
size_t mOutputBlockSize;
private:
static eWaifu2xError LoadMat(cv::Mat &float_image, const boost::filesystem::path &input_file, const int alpha_offset);
static eWaifu2xError LoadMatBySTBI(cv::Mat &float_image, const std::vector<char> &img_data);
static eWaifu2xError AlphaMakeBorder(std::vector<cv::Mat> &planes, const cv::Mat &alpha, const int offset);
eWaifu2xError CreateBrightnessImage(const cv::Mat &float_image, cv::Mat &im);
eWaifu2xError PaddingImage(const cv::Mat &input, cv::Mat &output);
eWaifu2xError Zoom2xAndPaddingImage(const cv::Mat &input, cv::Mat &output, cv::Size_<int> &zoom_size);
eWaifu2xError CreateZoomColorImage(const cv::Mat &float_image, const cv::Size_<int> &zoom_size, std::vector<cv::Mat> &cubic_planes);
eWaifu2xError ConstractNet(boost::shared_ptr<caffe::Net<float>> &net, const boost::filesystem::path &model_path, const boost::filesystem::path &param_path, const boost::filesystem::path &info_path, const std::string &process);
eWaifu2xError LoadParameterFromJson(boost::shared_ptr<caffe::Net<float>> &net, const boost::filesystem::path &model_path, const boost::filesystem::path &param_path
, const boost::filesystem::path &modelbin_path, const boost::filesystem::path &caffemodel_path, const std::string &process);
eWaifu2xError LoadInfoFromJson(const boost::filesystem::path &info_path);
eWaifu2xError SetParameter(caffe::NetParameter &param, const std::string &process) const;
eWaifu2xError ReconstructImage(boost::shared_ptr<caffe::Net<float>> net, const int reconstructed_scale, cv::Mat &im);
static eWaifu2xError WriteMat(const cv::Mat &im, const boost::filesystem::path &output_file, const boost::optional<int> &output_quality);
eWaifu2xError BeforeReconstructFloatMatProcess(const cv::Mat &in, cv::Mat &out, bool &convertBGRflag);
eWaifu2xError ReconstructFloatMat(const bool isReconstructNoise, const bool isReconstructScale, const waifu2xCancelFunc cancel_func, const cv::Mat &in, cv::Mat &out);
eWaifu2xError Reconstruct(const bool isReconstructNoise, const bool isReconstructScale, const waifu2xCancelFunc cancel_func, const cv::Mat &in, cv::Mat &out);
eWaifu2xError AfterReconstructFloatMatProcess(const bool isReconstructScale, const waifu2xCancelFunc cancel_func, const cv::Mat &floatim, cv::Mat &in, cv::Mat &out);
Waifu2x::eWaifu2xError ReconstructImage(const double factor, const int crop_w, const int crop_h, const bool use_tta, const int batch_size,
const bool isReconstructNoise, const bool isReconstructScale, const Waifu2x::waifu2xCancelFunc cancel_func, stImage &image);
Waifu2x::eWaifu2xError ReconstructScale(const int crop_w, const int crop_h, const bool use_tta, const int batch_size,
const Waifu2x::waifu2xCancelFunc cancel_func, stImage &image);
Waifu2x::eWaifu2xError ReconstructByNet(std::shared_ptr<cNet> net, const int crop_w, const int crop_h, const bool use_tta, const int batch_size,
const Waifu2x::waifu2xCancelFunc cancel_func, cv::Mat &im);
Waifu2x::eWaifu2xError ProcessNet(std::shared_ptr<cNet> net, const int crop_w, const int crop_h, const bool use_tta, const int batch_size, cv::Mat &im);
eWaifu2xError waifu2xConvetedMat(const bool isJpeg, const cv::Mat &inMat, cv::Mat &outMat,
const waifu2xCancelFunc cancel_func = nullptr);
double CalcScaleRatio(const cv::Size_<int> &size) const;
static int DepthBitToCVDepth(const int depth_bit);
static double GetValumeMaxFromCVDepth(const int cv_depth);
static double GetEPS(const int cv_depth);
// double CalcScaleRatio(const cv::Size_<int> &size) const;
public:
Waifu2x();
~Waifu2x();
static eWaifu2xcuDNNError can_use_cuDNN();
static eWaifu2xCudaError can_use_CUDA();
static eWaifu2xcuDNNError can_use_cuDNN();
static void init_liblary();
static void init_liblary(int argc, char** argv);
static void quit_liblary();
// mode: noise or scale or noise_scale or auto_scale
// process: cpu or gpu or cudnn
eWaifu2xError init(int argc, char** argv, const std::string &mode, const int noise_level,
const boost::optional<double> scale_ratio, const boost::optional<int> scale_width, const boost::optional<int> scale_height,
const boost::filesystem::path &model_dir, const std::string &process,
const boost::optional<int> output_quality = boost::optional<int>(), const int output_depth = 8, const bool use_tta = false,
const int crop_size = 128, const int batch_size = 1);
void destroy();
eWaifu2xError Init(const std::string &mode, const int noise_level,
const boost::filesystem::path &model_dir, const std::string &process);
eWaifu2xError waifu2x(const boost::filesystem::path &input_file, const boost::filesystem::path &output_file,
const waifu2xCancelFunc cancel_func = nullptr);
const double factor, const waifu2xCancelFunc cancel_func = nullptr, const int crop_w = 128, const int crop_h = 128,
const boost::optional<int> output_quality = boost::optional<int>(), const int output_depth = 8, const bool use_tta = false,
const int batch_size = 1);
// factor: 倍率
// source: (4チャンネルの場合は)RGBAな画素配列
// dest: (4チャンネルの場合は)処理したRGBAな画素配列
// in_stride: sourceのストライド(バイト単位)
// out_stride: destのストライド(バイト単位)
eWaifu2xError waifu2x(double factor, const void* source, void* dest, int width, int height, int in_channel, int in_stride, int out_channel, int out_stride);
eWaifu2xError waifu2x(const double factor, const void* source, void* dest, const int width, const int height,
const int in_channel, const int in_stride, const int out_channel, const int out_stride,
const int crop_w = 128, const int crop_h = 128, const bool use_tta = false, const int batch_size = 1);
void Destroy();
const std::string& used_process() const;
static cv::Mat LoadMat(const boost::filesystem::path &path);
};

View File

@ -14,6 +14,7 @@
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include "../common/waifu2x.h"
#include "../common/stImage.h"
#include "CDialog.h"
#include "CControl.h"
//#include <boost/program_options.hpp>
@ -295,7 +296,7 @@ bool DialogEvent::SyncMember(const bool NotSyncCropSize, const bool silent)
}
{
const auto &OutputExtentionList = Waifu2x::OutputExtentionList;
const auto &OutputExtentionList = stImage::OutputExtentionList;
const int cur = SendMessage(GetDlgItem(dh, IDC_COMBO_OUT_EXT), CB_GETCURSEL, 0, 0);
if (cur < 0 || cur >= OutputExtentionList.size())
@ -387,8 +388,9 @@ void DialogEvent::SetCropSizeList(const boost::filesystem::path & input_path)
int gcd = 1;
if (boost::filesystem::exists(input_path) && !boost::filesystem::is_directory(input_path))
{
auto mat = Waifu2x::LoadMat(input_path.string());
if (mat.empty())
cv::Mat mat;
const auto ret = stImage::LoadMat(mat, input_path.string());
if (ret != Waifu2x::eWaifu2xError_OK)
return;
auto size = mat.size();
@ -674,25 +676,8 @@ void DialogEvent::ProcessWaifu2x()
Waifu2x::eWaifu2xError ret;
boost::optional<double> ScaleRatio;
boost::optional<int> ScaleWidth;
boost::optional<int> ScaleHeight;
switch (scaleType)
{
case eScaleTypeRatio:
ScaleRatio = scale_ratio;
break;
case eScaleTypeWidth:
ScaleWidth = scale_width;
break;
default:
ScaleHeight = scale_height;
break;
}
Waifu2x w;
ret = w.init(__argc, __argv, mode, noise_level, ScaleRatio, ScaleWidth, ScaleHeight, model_dir, process, output_quality, output_depth, use_tta, crop_size, batch_size);
ret = w.Init(mode, noise_level, model_dir, process);
if (ret != Waifu2x::eWaifu2xError_OK)
SendMessage(dh, WM_ON_WAIFU2X_ERROR, (WPARAM)&ret, 0);
else
@ -723,10 +708,27 @@ void DialogEvent::ProcessWaifu2x()
continue;
}
ret = w.waifu2x(p.first, p.second, [this]()
double factor;
//switch (scaleType)
//{
//case eScaleTypeRatio:
// ScaleRatio = scale_ratio;
// break;
//case eScaleTypeWidth:
// ScaleWidth = scale_width;
// break;
//default:
// ScaleHeight = scale_height;
// break;
//}
factor = scale_ratio;
ret = w.waifu2x(p.first, p.second, factor, [this]()
{
return cancelFlag;
});
}, crop_size, crop_size, output_quality, output_depth, use_tta, batch_size);
num++;
ProgessFunc(maxFile, num);
@ -1441,7 +1443,7 @@ void DialogEvent::SetDepthAndQuality(const bool SetDefaultQuality)
if (cur < 0)
return;
const auto &OutputExtentionList = Waifu2x::OutputExtentionList;
const auto &OutputExtentionList = stImage::OutputExtentionList;
if (cur >= OutputExtentionList.size())
return;
@ -1583,7 +1585,7 @@ void DialogEvent::Create(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData)
{
HWND houtext = GetDlgItem(dh, IDC_COMBO_OUT_EXT);
const auto &OutputExtentionList = Waifu2x::OutputExtentionList;
const auto &OutputExtentionList = stImage::OutputExtentionList;
for (const auto &elm : OutputExtentionList)
{
SendMessageW(houtext, CB_ADDSTRING, 0, (LPARAM)elm.ext.c_str());
@ -1845,7 +1847,7 @@ void DialogEvent::Create(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData)
HWND houtext = GetDlgItem(dh, IDC_COMBO_OUT_EXT);
size_t defaultIndex = 0;
const auto &OutputExtentionList = Waifu2x::OutputExtentionList;
const auto &OutputExtentionList = stImage::OutputExtentionList;
for (size_t i = 0; i < OutputExtentionList.size(); i++)
{
const auto &elm = OutputExtentionList[i];

View File

@ -13,7 +13,7 @@ int WINAPI WinMain(HINSTANCE hInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
Waifu2x::init_liblary();
Waifu2x::init_liblary(__argc, __argv);
// 管理者権限で起動してもファイルのドロップを受け付けるようにする
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);

View File

@ -95,6 +95,8 @@
</Manifest>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\common\cNet.cpp" />
<ClCompile Include="..\common\stImage.cpp" />
<ClCompile Include="..\common\waifu2x.cpp" />
<ClCompile Include="CControl.cpp" />
<ClCompile Include="CDialog.cpp" />
@ -103,6 +105,8 @@
<ClCompile Include="Source.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\cNet.h" />
<ClInclude Include="..\common\stImage.h" />
<ClInclude Include="..\common\waifu2x.h" />
<ClInclude Include="CControl.h" />
<ClInclude Include="CDialog.h" />

View File

@ -13,14 +13,14 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="common">
<UniqueIdentifier>{8f85ea70-f834-4cfc-8869-629e95423258}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Source.cpp">
<Filter>ソース ファイル</Filter>
</ClCompile>
<ClCompile Include="..\common\waifu2x.cpp">
<Filter>ソース ファイル</Filter>
</ClCompile>
<ClCompile Include="CControl.cpp">
<Filter>ソース ファイル</Filter>
</ClCompile>
@ -33,11 +33,17 @@
<ClCompile Include="MainDialog.cpp">
<Filter>ソース ファイル</Filter>
</ClCompile>
<ClCompile Include="..\common\cNet.cpp">
<Filter>common</Filter>
</ClCompile>
<ClCompile Include="..\common\stImage.cpp">
<Filter>common</Filter>
</ClCompile>
<ClCompile Include="..\common\waifu2x.cpp">
<Filter>common</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\waifu2x.h">
<Filter>ヘッダー ファイル</Filter>
</ClInclude>
<ClInclude Include="CControl.h">
<Filter>ヘッダー ファイル</Filter>
</ClInclude>
@ -71,6 +77,15 @@
<ClInclude Include="MainDialog.h">
<Filter>ヘッダー ファイル</Filter>
</ClInclude>
<ClInclude Include="..\common\cNet.h">
<Filter>common</Filter>
</ClInclude>
<ClInclude Include="..\common\stImage.h">
<Filter>common</Filter>
</ClInclude>
<ClInclude Include="..\common\waifu2x.h">
<Filter>common</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc">