#define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "resource.h" #include "../common/waifu2x.h" #include "CDialog.h" #include "CControl.h" #undef ERROR #define WM_FAILD_CREATE_DIR (WM_APP + 5) #define WM_ON_WAIFU2X_ERROR (WM_APP + 6) #define WM_END_THREAD (WM_APP + 7) const size_t AR_PATH_MAX(1024); const int MinCommonDivisor = 50; const int DefaultCommonDivisor = 128; const std::pair DefaultCommonDivisorRange = {90, 140}; const TCHAR * const CropSizeListName = TEXT("crop_size_list.txt"); const TCHAR * const SettingFileName = TEXT("setting.ini"); #ifdef UNICODE typedef std::wstring tstring; inline tstring getTString(const boost::filesystem::path& p) { return p.wstring(); } #else typedef std::string tstring; inline tstring getTString(const boost::filesystem::path& p) { return p.string(); } #endif // http://stackoverflow.com/questions/10167382/boostfilesystem-get-relative-path boost::filesystem::path relativePath(const boost::filesystem::path &path, const boost::filesystem::path &relative_to) { // create absolute paths boost::filesystem::path p = boost::filesystem::absolute(path); boost::filesystem::path r = boost::filesystem::absolute(relative_to); // if root paths are different, return absolute path if (p.root_path() != r.root_path()) return p; // initialize relative path boost::filesystem::path result; // find out where the two paths diverge boost::filesystem::path::const_iterator itr_path = p.begin(); boost::filesystem::path::const_iterator itr_relative_to = r.begin(); while (*itr_path == *itr_relative_to && itr_path != p.end() && itr_relative_to != r.end()) { ++itr_path; ++itr_relative_to; } // add "../" for each remaining token in relative_to if (itr_relative_to != r.end()) { ++itr_relative_to; while (itr_relative_to != r.end()) { result /= ".."; ++itr_relative_to; } } // add remaining path while (itr_path != p.end()) { result /= *itr_path; ++itr_path; } return result; } std::vector CommonDivisorList(const int N) { std::vector list; const int sq = sqrt(N); for (int i = 1; i <= sq; i++) { if (N % i == 0) list.push_back(i); } const int sqs = list.size(); for (int i = 0; i < sqs; i++) list.push_back(N / list[i]); std::sort(list.begin(), list.end()); return list; } // ダイアログ用 class DialogEvent { private: HWND dh; boost::filesystem::path exeDir; std::vector CropSizeList; tstring input_str; tstring output_str; std::string mode; int noise_level; double scale_ratio; tstring model_dir; std::string process; tstring outputExt; tstring inputFileExt; bool use_tta; int crop_size; int batch_size; std::vector extList; std::thread processThread; std::atomic_bool cancelFlag; tstring autoSetAddName; bool isLastError; tstring logMessage; std::string usedProcess; std::chrono::system_clock::duration cuDNNCheckTime; std::chrono::system_clock::duration InitTime; std::chrono::system_clock::duration ProcessTime; enum eModelType { eModelTypeRGB, eModelTypePhoto, eModelTypeY, eModelTypeEnd, }; eModelType modelType; private: template static tstring to_tstring(T val) { #ifdef UNICODE return std::to_wstring(val); #else return std::to_string(val); #endif } tstring AddName() const { tstring addstr; addstr += TEXT("("); switch (modelType) { case eModelTypeRGB: addstr += TEXT("RGB"); break; case eModelTypePhoto: addstr += TEXT("Photo"); break; case eModelTypeY: addstr += TEXT("Y"); break; } addstr += TEXT(")"); addstr += TEXT("("); if (mode == "noise") addstr += TEXT("noise"); else if (mode == "scale") addstr += TEXT("scale"); else if (mode == "noise_scale") addstr += TEXT("noise_scale"); else if (mode == "auto_scale") addstr += TEXT("auto_scale"); addstr += TEXT(")"); if (mode.find("noise") != mode.npos || mode.find("auto_scale") != mode.npos) addstr += TEXT("(Level") + to_tstring(noise_level) + TEXT(")"); if (use_tta) addstr += TEXT("(tta)"); if (mode.find("scale") != mode.npos) addstr += TEXT("(x") + to_tstring(scale_ratio) + TEXT(")"); return addstr; } bool SyncMember(const bool NotSyncCropSize) { bool ret = true; { TCHAR buf[AR_PATH_MAX] = TEXT(""); GetWindowText(GetDlgItem(dh, IDC_EDIT_INPUT), buf, _countof(buf)); buf[_countof(buf) - 1] = TEXT('\0'); input_str = buf; } { TCHAR buf[AR_PATH_MAX] = TEXT(""); GetWindowText(GetDlgItem(dh, IDC_EDIT_OUTPUT), buf, _countof(buf)); buf[_countof(buf) - 1] = TEXT('\0'); output_str = buf; } if (SendMessage(GetDlgItem(dh, IDC_RADIO_MODE_NOISE), BM_GETCHECK, 0, 0)) mode = "noise"; else if (SendMessage(GetDlgItem(dh, IDC_RADIO_MODE_SCALE), BM_GETCHECK, 0, 0)) mode = "scale"; else if (SendMessage(GetDlgItem(dh, IDC_RADIO_MODE_NOISE_SCALE), BM_GETCHECK, 0, 0)) mode = "noise_scale"; else mode = "auto_scale"; if (SendMessage(GetDlgItem(dh, IDC_RADIONOISE_LEVEL1), BM_GETCHECK, 0, 0)) noise_level = 1; else noise_level = 2; { TCHAR buf[AR_PATH_MAX] = TEXT(""); GetWindowText(GetDlgItem(dh, IDC_EDIT_SCALE_RATIO), buf, _countof(buf)); buf[_countof(buf) - 1] = TEXT('\0'); TCHAR *ptr = nullptr; scale_ratio = _tcstod(buf, &ptr); if (!ptr || *ptr != TEXT('\0') || scale_ratio <= 0.0) { scale_ratio = 2.0; ret = false; MessageBox(dh, TEXT("拡大率は0.0より大きい正数である必要があります"), TEXT("エラー"), MB_OK | MB_ICONERROR); } } if (SendMessage(GetDlgItem(dh, IDC_RADIO_MODEL_RGB), BM_GETCHECK, 0, 0)) { model_dir = TEXT("models/anime_style_art_rgb"); modelType = eModelTypeRGB; } else if (SendMessage(GetDlgItem(dh, IDC_RADIO_MODEL_Y), BM_GETCHECK, 0, 0)) { model_dir = TEXT("models/anime_style_art"); modelType = eModelTypeY; } else { model_dir = TEXT("models/photo"); modelType = eModelTypePhoto; } { TCHAR buf[AR_PATH_MAX] = TEXT(""); GetWindowText(GetDlgItem(dh, IDC_EDIT_OUT_EXT), buf, _countof(buf)); buf[_countof(buf) - 1] = TEXT('\0'); outputExt = buf; if (outputExt.length() > 0 && outputExt[0] != TEXT('.')) outputExt = TEXT(".") + outputExt; } if (SendMessage(GetDlgItem(dh, IDC_RADIO_MODE_CPU), BM_GETCHECK, 0, 0)) process = "cpu"; else process = "gpu"; { TCHAR buf[AR_PATH_MAX] = TEXT(""); GetWindowText(GetDlgItem(dh, IDC_EDIT_INPUT_EXT_LIST), buf, _countof(buf)); buf[_countof(buf) - 1] = TEXT('\0'); inputFileExt = buf; // input_extention_listを文字列の配列にする typedef boost::char_separator char_separator; typedef boost::tokenizer tokenizer; char_separator sep(TEXT(":"), TEXT(""), boost::drop_empty_tokens); tokenizer tokens(inputFileExt, sep); for (auto tok_iter = tokens.begin(); tok_iter != tokens.end(); ++tok_iter) { tstring ext(*tok_iter); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); extList.push_back(TEXT(".") + ext); } } if (!NotSyncCropSize) { TCHAR buf[AR_PATH_MAX] = TEXT(""); GetWindowText(GetDlgItem(dh, IDC_COMBO_CROP_SIZE), buf, _countof(buf)); buf[_countof(buf) - 1] = TEXT('\0'); TCHAR *ptr = nullptr; crop_size = _tcstol(buf, &ptr, 10); if (!ptr || *ptr != '\0' || crop_size <= 0) { crop_size = 128; ret = false; MessageBox(dh, TEXT("分割サイズは0より大きい整数である必要があります"), TEXT("エラー"), MB_OK | MB_ICONERROR); } } use_tta = SendMessage(GetDlgItem(dh, IDC_CHECK_TTA), BM_GETCHECK, 0, 0) == BST_CHECKED; return ret; } void SetCropSizeList(const boost::filesystem::path &input_path) { HWND hcrop = GetDlgItem(dh, IDC_COMBO_CROP_SIZE); int gcd = 1; if (boost::filesystem::is_directory(input_path)) { BOOST_FOREACH(const boost::filesystem::path& p, std::make_pair(boost::filesystem::recursive_directory_iterator(input_path), boost::filesystem::recursive_directory_iterator())) { if (!boost::filesystem::is_directory(p)) { tstring ext(getTString(p.extension())); #ifdef UNICODE std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower); #else std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); #endif if (std::find(extList.begin(), extList.end(), ext) != extList.end()) { auto mat = Waifu2x::LoadMat(p.string()); if (mat.empty()) continue; auto size = mat.size(); mat.release(); gcd = boost::math::gcd(size.width, size.height); } } } } else { auto mat = Waifu2x::LoadMat(input_path.string()); if (mat.empty()) return; auto size = mat.size(); mat.release(); gcd = boost::math::gcd(size.width, size.height); } while (SendMessage(hcrop, CB_GETCOUNT, 0, 0) != 0) SendMessage(hcrop, CB_DELETESTRING, 0, 0); // 最大公約数の約数のリスト取得 std::vector list(CommonDivisorList(gcd)); // MinCommonDivisor未満の約数削除 list.erase(std::remove_if(list.begin(), list.end(), [](const int v) { return v < MinCommonDivisor; } ), list.end()); int mindiff = INT_MAX; int defaultIndex = -1; for (int i = 0; i < list.size(); i++) { const int n = list[i]; tstring str(to_tstring(n)); SendMessage(hcrop, CB_ADDSTRING, 0, (LPARAM)str.c_str()); const int diff = abs(DefaultCommonDivisor - n); if (DefaultCommonDivisorRange.first <= n && n <= DefaultCommonDivisorRange.second && diff < mindiff) { mindiff = diff; defaultIndex = i; } } SendMessage(hcrop, CB_ADDSTRING, 0, (LPARAM)TEXT("-----------------------")); // CropSizeListの値を追加していく mindiff = INT_MAX; int defaultListIndex = -1; for (const auto n : CropSizeList) { tstring str(to_tstring(n)); const int index = SendMessage(hcrop, CB_ADDSTRING, 0, (LPARAM)str.c_str()); const int diff = abs(DefaultCommonDivisor - n); if (DefaultCommonDivisorRange.first <= n && n <= DefaultCommonDivisorRange.second && diff < mindiff) { mindiff = diff; defaultListIndex = index; } } if (defaultIndex == -1) defaultIndex = defaultListIndex; if (GetWindowTextLength(hcrop) == 0) SendMessage(hcrop, CB_SETCURSEL, defaultIndex, 0); } void ProcessWaifu2x() { const boost::filesystem::path input_path(boost::filesystem::absolute(input_str)); std::vector> file_paths; if (boost::filesystem::is_directory(input_path)) // input_pathがフォルダならそのディレクトリ以下の画像ファイルを一括変換 { boost::filesystem::path output_path(output_str); output_path = boost::filesystem::absolute(output_path); if (!boost::filesystem::exists(output_path)) { if (!boost::filesystem::create_directory(output_path)) { SendMessage(dh, WM_FAILD_CREATE_DIR, (WPARAM)&output_path, 0); PostMessage(dh, WM_END_THREAD, 0, 0); // printf("出力フォルダ「%s」の作成に失敗しました\n", output_path.string().c_str()); return; } } // 変換する画像の入力、出力パスを取得 const auto func = [this, &input_path, &output_path, &file_paths](const boost::filesystem::path &path) { BOOST_FOREACH(const boost::filesystem::path& p, std::make_pair(boost::filesystem::recursive_directory_iterator(path), boost::filesystem::recursive_directory_iterator())) { if (!boost::filesystem::is_directory(p)) { tstring ext(getTString(p.extension())); #ifdef UNICODE std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower); #else std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); #endif if (std::find(extList.begin(), extList.end(), ext) != extList.end()) { const auto out_relative = relativePath(p, input_path); const auto out_absolute = output_path / out_relative; const auto out = getTString(out_absolute.branch_path() / out_absolute.stem()) + outputExt; file_paths.emplace_back(getTString(p), out); } } } return true; }; if (!func(input_path)) return; for (const auto &p : file_paths) { const boost::filesystem::path out_path(p.second); const boost::filesystem::path out_dir(out_path.parent_path()); if (!boost::filesystem::exists(out_dir)) { if (!boost::filesystem::create_directories(out_dir)) { SendMessage(dh, WM_FAILD_CREATE_DIR, (WPARAM)&out_dir, 0); PostMessage(dh, WM_END_THREAD, 0, 0); //printf("出力フォルダ「%s」の作成に失敗しました\n", out_absolute.string().c_str()); return; } } } } else file_paths.emplace_back(input_str, output_str); bool isFirst = true; const auto ProgessFunc = [this, &isFirst](const int ProgressFileMax, const int ProgressFileNow) { if (isFirst) { isFirst = true; SendMessage(GetDlgItem(dh, IDC_PROGRESS), PBM_SETRANGE32, 0, ProgressFileMax); } SendMessage(GetDlgItem(dh, IDC_PROGRESS), PBM_SETPOS, ProgressFileNow, 0); }; const auto cuDNNCheckStartTime = std::chrono::system_clock::now(); if (process == "gpu") Waifu2x::can_use_cuDNN(); const auto cuDNNCheckEndTime = std::chrono::system_clock::now(); Waifu2x::eWaifu2xError ret; Waifu2x w; ret = w.init(__argc, __argv, mode, noise_level, scale_ratio, model_dir, process, use_tta, crop_size, batch_size); if(ret != Waifu2x::eWaifu2xError_OK) SendMessage(dh, WM_ON_WAIFU2X_ERROR, (WPARAM)&ret, 0); else { const auto InitEndTime = std::chrono::system_clock::now(); for (const auto &p : file_paths) { ret = w.waifu2x(p.first, p.second, [this]() { return cancelFlag; }); if (ret != Waifu2x::eWaifu2xError_OK) { SendMessage(dh, WM_ON_WAIFU2X_ERROR, (WPARAM)&ret, (LPARAM)&p); if (ret == Waifu2x::eWaifu2xError_Cancel) break; } } const auto ProcessEndTime = std::chrono::system_clock::now(); cuDNNCheckTime = cuDNNCheckEndTime - cuDNNCheckStartTime; InitTime = InitEndTime - cuDNNCheckEndTime; ProcessTime = ProcessEndTime - InitEndTime; usedProcess = w.used_process(); } PostMessage(dh, WM_END_THREAD, 0, 0); } void ReplaceAddString() // ファイル名の自動設定部分を書き換える { SyncMember(true); const boost::filesystem::path output_path(output_str); tstring stem; if (!boost::filesystem::is_directory(input_str)) stem = getTString(output_path.stem()); else stem = getTString(output_path.filename()); if (stem.length() > 0 && stem.length() >= autoSetAddName.length()) { const auto pos = stem.find(autoSetAddName); if (pos != tstring::npos) { const tstring addstr(AddName()); auto new_name = stem; new_name.replace(pos, autoSetAddName.length(), addstr); autoSetAddName = addstr; boost::filesystem::path new_out_path; if (!boost::filesystem::is_directory(input_str)) new_out_path = output_path.branch_path() / (new_name + outputExt); else new_out_path = output_path.branch_path() / (new_name); SetWindowText(GetDlgItem(dh, IDC_EDIT_OUTPUT), getTString(new_out_path).c_str()); } } } void AddLogMessage(const TCHAR *msg) { if (logMessage.length() == 0) logMessage += msg; else logMessage += tstring(TEXT("\r\n")) + msg; SetWindowText(GetDlgItem(dh, IDC_EDIT_LOG), logMessage.c_str()); } void Waifu2xTime() { TCHAR msg[1024 * 2]; TCHAR *ptr = msg; { tstring p; if (usedProcess == "cpu") p = TEXT("CPU"); else if (usedProcess == "gpu") p = TEXT("CUDA"); else // if (p == "cudnn") p = TEXT("cuDNN"); ptr += _stprintf(ptr, TEXT("使用プロセッサーモード: %s\r\n"), p.c_str()); } { uint64_t t = std::chrono::duration_cast(ProcessTime).count(); const int msec = t % 1000; t /= 1000; const int sec = t % 60; t /= 60; const int min = t % 60; t /= 60; const int hour = (int)t; ptr += _stprintf(ptr, TEXT("処理時間: %02d:%02d:%02d.%d\r\n"), hour, min, sec, msec); } { uint64_t t = std::chrono::duration_cast(InitTime).count(); const int msec = t % 1000; t /= 1000; const int sec = t % 60; t /= 60; const int min = t % 60; t /= 60; const int hour = (int)t; ptr += _stprintf(ptr, TEXT("初期化時間: %02d:%02d:%02d.%d\r\n"), hour, min, sec, msec); } if (process == "gpu" || process == "cudnn") { uint64_t t = std::chrono::duration_cast(cuDNNCheckTime).count(); const int msec = t % 1000; t /= 1000; const int sec = t % 60; t /= 60; const int min = t % 60; t /= 60; const int hour = (int)t; ptr += _stprintf(ptr, TEXT("cuDNNチェック時間: %02d:%02d:%02d.%d"), hour, min, sec, msec); } AddLogMessage(msg); } void SaveIni(const bool isSyncMember = true) { if (isSyncMember) SyncMember(true); const boost::filesystem::path SettingFilePath(exeDir / SettingFileName); tstring tScale; tstring tmode; tstring tprcess; tstring toutputExt; tScale = to_tstring(scale_ratio); if (mode == ("noise")) tmode = TEXT("noise"); else if (mode == ("scale")) tmode = TEXT("scale"); else if (mode == ("auto_scale")) tmode = TEXT("auto_scale"); else // noise_scale tmode = TEXT("noise_scale"); if (process == "gpu") tprcess = TEXT("gpu"); else tprcess = TEXT("cpu"); toutputExt = outputExt; if (toutputExt.length() > 0 && toutputExt[0] == TEXT('.')) toutputExt.erase(0, 1); WritePrivateProfileString(TEXT("Setting"), TEXT("LastScale"), tScale.c_str(), getTString(SettingFilePath).c_str()); WritePrivateProfileString(TEXT("Setting"), TEXT("LastOutputExt"), toutputExt.c_str(), getTString(SettingFilePath).c_str()); WritePrivateProfileString(TEXT("Setting"), TEXT("LastInputFileExt"), inputFileExt.c_str(), getTString(SettingFilePath).c_str()); WritePrivateProfileString(TEXT("Setting"), TEXT("LastMode"), tmode.c_str(), getTString(SettingFilePath).c_str()); WritePrivateProfileString(TEXT("Setting"), TEXT("LastNoiseLevel"), to_tstring(noise_level).c_str(), getTString(SettingFilePath).c_str()); WritePrivateProfileString(TEXT("Setting"), TEXT("LastProcess"), tprcess.c_str(), getTString(SettingFilePath).c_str()); WritePrivateProfileString(TEXT("Setting"), TEXT("LastModel"), to_tstring(modelType).c_str(), getTString(SettingFilePath).c_str()); WritePrivateProfileString(TEXT("Setting"), TEXT("LastUseTTA"), to_tstring(use_tta ? 1 : 0).c_str(), getTString(SettingFilePath).c_str()); } public: DialogEvent() : dh(nullptr), mode("noise_scale"), noise_level(1), scale_ratio(2.0), model_dir(TEXT("models/anime_style_art_rgb")), process("gpu"), outputExt(TEXT("png")), inputFileExt(TEXT("png:jpg:jpeg:tif:tiff:bmp:tga")), use_tta(false), crop_size(128), batch_size(1), isLastError(false) { } void Exec(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { if (processThread.joinable()) return; if (!SyncMember(false)) return; if (input_str.length() == 0) { MessageBox(dh, TEXT("入力パスを指定して下さい"), TEXT("エラー"), MB_OK | MB_ICONERROR); return; } if (output_str.length() == 0) { MessageBox(dh, TEXT("出力パスを指定して下さい"), TEXT("エラー"), MB_OK | MB_ICONERROR); return; } if (outputExt.length() == 0) { MessageBox(dh, TEXT("出力拡張子を指定して下さい"), TEXT("エラー"), MB_OK | MB_ICONERROR); return; } if (process == "gpu" || process == "cudnn") { const auto flag = Waifu2x::can_use_CUDA(); switch (flag) { case Waifu2x::eWaifu2xCudaError_NotFind: MessageBox(dh, TEXT("GPUで変換出来ません。\r\nCUDAドライバーがインストールされていない可能性があります。\r\nCUDAドライバーをインストールして下さい。"), TEXT("エラー"), MB_OK | MB_ICONERROR); return; case Waifu2x::eWaifu2xCudaError_OldVersion: MessageBox(dh, TEXT("GPUで変換出来ません。\r\nCUDAドライバーのバージョンが古い可能性があります。\r\nCUDAドライバーを更新して下さい。"), TEXT("エラー"), MB_OK | MB_ICONERROR); return; } } SaveIni(true); // 強制終了の可能性も考えて実行時に設定保存 SendMessage(GetDlgItem(dh, IDC_PROGRESS), PBM_SETPOS, 0, 0); cancelFlag = false; isLastError = false; processThread = std::thread(std::bind(&DialogEvent::ProcessWaifu2x, this)); EnableWindow(GetDlgItem(dh, IDC_BUTTON_CANCEL), TRUE); EnableWindow(GetDlgItem(dh, IDC_BUTTON_EXEC), FALSE); EnableWindow(GetDlgItem(dh, IDC_BUTTON_CHECK_CUDNN), FALSE); SetWindowText(GetDlgItem(hWnd, IDC_EDIT_LOG), TEXT("")); logMessage.clear(); } void WaitThreadExit(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { processThread.join(); EnableWindow(GetDlgItem(dh, IDC_BUTTON_CANCEL), FALSE); EnableWindow(GetDlgItem(dh, IDC_BUTTON_EXEC), TRUE); EnableWindow(GetDlgItem(dh, IDC_BUTTON_CHECK_CUDNN), TRUE); if (!isLastError) { if (!cancelFlag) AddLogMessage(TEXT("変換に成功しました")); Waifu2xTime(); MessageBeep(MB_ICONASTERISK); } else MessageBox(dh, TEXT("エラーが発生しました"), TEXT("エラー"), MB_OK | MB_ICONERROR); } void OnDialogEnd(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { SaveIni(); if (!processThread.joinable()) PostQuitMessage(0); else MessageBeep(MB_ICONEXCLAMATION); } void OnFaildCreateDir(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { const boost::filesystem::path *p = (const boost::filesystem::path *)wParam; // 出力フォルダ「%s」の作成に失敗しました\n", out_absolute.string().c_str()); tstring msg(TEXT("出力フォルダ\r\n「")); msg += getTString(*p); msg += TEXT("」\r\nの作成に失敗しました"); MessageBox(dh, msg.c_str(), TEXT("エラー"), MB_OK | MB_ICONERROR); isLastError = true; } void OnWaifu2xError(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { const Waifu2x::eWaifu2xError ret = *(const Waifu2x::eWaifu2xError *)wParam; if (ret != Waifu2x::eWaifu2xError_OK) { TCHAR msg[1024] = TEXT(""); if (lParam == 0) { switch (ret) { case Waifu2x::eWaifu2xError_Cancel: _stprintf(msg, TEXT("キャンセルされました")); break; case Waifu2x::eWaifu2xError_InvalidParameter: _stprintf(msg, TEXT("パラメータが不正です")); break; case Waifu2x::eWaifu2xError_FailedOpenModelFile: _stprintf(msg, TEXT("モデルファイルが開けませんでした")); break; case Waifu2x::eWaifu2xError_FailedParseModelFile: _stprintf(msg, TEXT("モデルファイルが壊れています")); break; case Waifu2x::eWaifu2xError_FailedConstructModel: _stprintf(msg, TEXT("ネットワークの構築に失敗しました")); break; } } else { const auto &fp = *(const std::pair *)lParam; switch (ret) { case Waifu2x::eWaifu2xError_Cancel: _stprintf(msg, TEXT("キャンセルされました")); break; case Waifu2x::eWaifu2xError_InvalidParameter: _stprintf(msg, TEXT("パラメータが不正です")); break; case Waifu2x::eWaifu2xError_FailedOpenInputFile: _stprintf(msg, TEXT("入力画像「%s」が開けませんでした"), fp.first.c_str()); break; case Waifu2x::eWaifu2xError_FailedOpenOutputFile: _stprintf(msg, TEXT("出力画像を「%s」に書き込めませんでした"), fp.second.c_str()); break; case Waifu2x::eWaifu2xError_FailedProcessCaffe: _stprintf(msg, TEXT("補間処理に失敗しました")); break; } } AddLogMessage(msg); if (ret != Waifu2x::eWaifu2xError_Cancel) isLastError = true; } } void Create(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { dh = hWnd; { TCHAR texepath[1024 * 3] = TEXT(""); GetModuleFileName(NULL, texepath, _countof(texepath)); texepath[_countof(texepath) - 1] = TEXT('\0'); const boost::filesystem::path exePath(texepath); exeDir = exePath.branch_path(); } const boost::filesystem::path CropSizeListPath(exeDir / CropSizeListName); std::ifstream ifs(CropSizeListPath.wstring()); if (ifs) { std::string str; while (getline(ifs, str)) { char *ptr = nullptr; const long n = strtol(str.c_str(), &ptr, 10); if (ptr && *ptr == '\0') CropSizeList.push_back(n); } } { HWND hcrop = GetDlgItem(dh, IDC_COMBO_CROP_SIZE); SendMessage(hcrop, CB_ADDSTRING, 0, (LPARAM)TEXT("-----------------------")); // CropSizeListの値を追加していく int mindiff = INT_MAX; int defaultListIndex = -1; for (const auto n : CropSizeList) { tstring str(to_tstring(n)); const int index = SendMessage(hcrop, CB_ADDSTRING, 0, (LPARAM)str.c_str()); const int diff = abs(DefaultCommonDivisor - n); if (DefaultCommonDivisorRange.first <= n && n <= DefaultCommonDivisorRange.second && diff < mindiff) { mindiff = diff; defaultListIndex = index; } } if (GetWindowTextLength(hcrop) == 0) SendMessage(hcrop, CB_SETCURSEL, defaultListIndex, 0); } const boost::filesystem::path SettingFilePath(exeDir / SettingFileName); tstring tScale; tstring tmode; tstring tprcess; { TCHAR tmp[1000]; GetPrivateProfileString(TEXT("Setting"), TEXT("LastScale"), TEXT("2.00"), tmp, _countof(tmp), getTString(SettingFilePath).c_str()); tmp[_countof(tmp) - 1] = TEXT('\0'); tScale = tmp; GetPrivateProfileString(TEXT("Setting"), TEXT("LastOutputExt"), TEXT("png"), tmp, _countof(tmp), getTString(SettingFilePath).c_str()); tmp[_countof(tmp) - 1] = TEXT('\0'); outputExt = tmp; GetPrivateProfileString(TEXT("Setting"), TEXT("LastInputFileExt"), TEXT("png:jpg:jpeg:tif:tiff:bmp:tga"), tmp, _countof(tmp), getTString(SettingFilePath).c_str()); tmp[_countof(tmp) - 1] = TEXT('\0'); inputFileExt = tmp; GetPrivateProfileString(TEXT("Setting"), TEXT("LastMode"), TEXT("noise_scale"), tmp, _countof(tmp), getTString(SettingFilePath).c_str()); tmp[_countof(tmp) - 1] = TEXT('\0'); tmode = tmp; noise_level = GetPrivateProfileInt(TEXT("Setting"), TEXT("LastNoiseLevel"), 1, getTString(SettingFilePath).c_str()); GetPrivateProfileString(TEXT("Setting"), TEXT("LastProcess"), TEXT("gpu"), tmp, _countof(tmp), getTString(SettingFilePath).c_str()); tmp[_countof(tmp) - 1] = TEXT('\0'); tprcess = tmp; modelType = (eModelType)GetPrivateProfileInt(TEXT("Setting"), TEXT("LastModel"), 0, getTString(SettingFilePath).c_str()); use_tta = GetPrivateProfileInt(TEXT("Setting"), TEXT("LastUseTTA"), 0, getTString(SettingFilePath).c_str()) != 0; } TCHAR *ptr = nullptr; const double tempScale = _tcstod(tScale.c_str(), &ptr); if (!ptr || *ptr != TEXT('\0') || tempScale <= 0.0) tScale = TEXT("2.00"); if (outputExt.length() > 0 && outputExt[0] == TEXT('.')) outputExt.erase(0, 1); if (!(1 <= noise_level && noise_level <= 2)) noise_level = 1; if (tprcess == TEXT("gpu")) process = "gpu"; else process = "cpu"; if (!((eModelType)0 <= modelType && modelType < eModelTypeEnd)) modelType = eModelTypeRGB; if (tmode == TEXT("noise")) { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE), BM_SETCHECK, BST_CHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_AUTO_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); } else if (tmode == TEXT("scale")) { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_SCALE), BM_SETCHECK, BST_CHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_AUTO_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); } else if (tmode == TEXT("auto_scale")) { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_AUTO_SCALE), BM_SETCHECK, BST_CHECKED, 0); } else // noise_scale { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE_SCALE), BM_SETCHECK, BST_CHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_NOISE), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_AUTO_SCALE), BM_SETCHECK, BST_UNCHECKED, 0); } if (noise_level == 1) { SendMessage(GetDlgItem(hWnd, IDC_RADIONOISE_LEVEL1), BM_SETCHECK, BST_CHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIONOISE_LEVEL2), BM_SETCHECK, BST_UNCHECKED, 0); } else { SendMessage(GetDlgItem(hWnd, IDC_RADIONOISE_LEVEL1), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIONOISE_LEVEL2), BM_SETCHECK, BST_CHECKED, 0); } if (process == "gpu") { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_GPU), BM_SETCHECK, BST_CHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_CPU), BM_SETCHECK, BST_UNCHECKED, 0); } else { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_GPU), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODE_CPU), BM_SETCHECK, BST_CHECKED, 0); } if (modelType == eModelTypeRGB) { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_RGB), BM_SETCHECK, BST_CHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_PHOTO), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_Y), BM_SETCHECK, BST_UNCHECKED, 0); } else if (modelType == eModelTypePhoto) { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_RGB), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_PHOTO), BM_SETCHECK, BST_CHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_Y), BM_SETCHECK, BST_UNCHECKED, 0); } else { SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_RGB), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_PHOTO), BM_SETCHECK, BST_UNCHECKED, 0); SendMessage(GetDlgItem(hWnd, IDC_RADIO_MODEL_Y), BM_SETCHECK, BST_CHECKED, 0); } if (use_tta) SendMessage(GetDlgItem(hWnd, IDC_CHECK_TTA), BM_SETCHECK, BST_CHECKED, 0); else SendMessage(GetDlgItem(hWnd, IDC_CHECK_TTA), BM_SETCHECK, BST_UNCHECKED, 0); SetWindowText(GetDlgItem(hWnd, IDC_EDIT_SCALE_RATIO), tScale.c_str()); SetWindowText(GetDlgItem(hWnd, IDC_EDIT_OUT_EXT), outputExt.c_str()); SetWindowText(GetDlgItem(hWnd, IDC_EDIT_INPUT_EXT_LIST), inputFileExt.c_str()); EnableWindow(GetDlgItem(dh, IDC_BUTTON_CANCEL), FALSE); } void Cancel(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { cancelFlag = true; EnableWindow(GetDlgItem(dh, IDC_BUTTON_CANCEL), FALSE); } void RadioButtom(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { ReplaceAddString(); } void CheckCUDNN(HWND hWnd, WPARAM wParam, LPARAM lParam, LPVOID lpData) { const auto flag = Waifu2x::can_use_CUDA(); switch (flag) { case Waifu2x::eWaifu2xCudaError_NotFind: MessageBox(dh, TEXT("cuDNNは使えません。\r\nCUDAドライバーがインストールされていない可能性があります。\r\nCUDAドライバーをインストールして下さい。"), TEXT("結果"), MB_OK | MB_ICONERROR); return; case Waifu2x::eWaifu2xCudaError_OldVersion: MessageBox(dh, TEXT("cuDNNは使えません。\r\nCUDAドライバーのバージョンが古い可能性があります。\r\nCUDAドライバーを更新して下さい。"), TEXT("結果"), MB_OK | MB_ICONERROR); return; } switch (Waifu2x::can_use_cuDNN()) { case Waifu2x::eWaifu2xcuDNNError_OK: MessageBox(dh, TEXT("cuDNNが使えます。"), TEXT("結果"), MB_OK | MB_ICONINFORMATION); break; case Waifu2x::eWaifu2xcuDNNError_NotFind: MessageBox(dh, TEXT("cuDNNは使えません。\r\n「") TEXT(CUDNN_DLL_NAME) TEXT("」が見つかりません。"), TEXT("結果"), MB_OK | MB_ICONERROR); break; case Waifu2x::eWaifu2xcuDNNError_OldVersion: MessageBox(dh, TEXT("cuDNNは使えません。\r\n「") TEXT(CUDNN_DLL_NAME) TEXT("」のバージョンが古いです。v2を使って下さい。"), TEXT("結果"), MB_OK | MB_ICONERROR); break; case Waifu2x::eWaifu2xcuDNNError_CannotCreate: MessageBox(dh, TEXT("cuDNNは使えません。\r\ncuDNNを初期化出来ません。"), TEXT("結果"), MB_OK | MB_ICONERROR); break; default: MessageBox(dh, TEXT("cuDNNは使えません"), TEXT("結果"), MB_OK | MB_ICONERROR); } } // ここで渡されるhWndはIDC_EDITのHWND(コントロールのイベントだから) LRESULT DropInput(HWND hWnd, WPARAM wParam, LPARAM lParam, WNDPROC OrgSubWnd, LPVOID lpData) { TCHAR szTmp[AR_PATH_MAX]; // ドロップされたファイル数を取得 UINT FileNum = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, szTmp, _countof(szTmp)); if (FileNum >= 1) { DragQueryFile((HDROP)wParam, 0, szTmp, _countof(szTmp)); boost::filesystem::path path(szTmp); if (!boost::filesystem::exists(path)) { MessageBox(dh, TEXT("入力ファイル/フォルダが存在しません"), TEXT("エラー"), MB_OK | MB_ICONERROR); return 0L; } if (!SyncMember(true)) return 0L; if (boost::filesystem::is_directory(path)) { HWND ho = GetDlgItem(dh, IDC_EDIT_OUTPUT); const tstring addstr(AddName()); autoSetAddName = AddName(); const auto str = getTString(path.branch_path() / (path.stem().wstring() + addstr)); SetWindowText(ho, str.c_str()); SetWindowText(hWnd, szTmp); } else { HWND ho = GetDlgItem(dh, IDC_EDIT_OUTPUT); tstring outputFileName = szTmp; const auto tailDot = outputFileName.find_last_of('.'); if (tailDot != outputFileName.npos) outputFileName.erase(tailDot, outputFileName.length()); const tstring addstr(AddName()); autoSetAddName = addstr; outputFileName += addstr + outputExt; SetWindowText(ho, outputFileName.c_str()); SetWindowText(hWnd, szTmp); } SetCropSizeList(path); } return 0L; } // ここで渡されるhWndはIDC_EDITのHWND(コントロールのイベントだから) LRESULT DropOutput(HWND hWnd, WPARAM wParam, LPARAM lParam, WNDPROC OrgSubWnd, LPVOID lpData) { TCHAR szTmp[AR_PATH_MAX]; // ドロップされたファイル数を取得 UINT FileNum = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, szTmp, AR_PATH_MAX); if (FileNum >= 1) { DragQueryFile((HDROP)wParam, 0, szTmp, AR_PATH_MAX); SetWindowText(hWnd, szTmp); } return 0L; } LRESULT TextInput(HWND hWnd, WPARAM wParam, LPARAM lParam, WNDPROC OrgSubWnd, LPVOID lpData) { const auto ret = CallWindowProc(OrgSubWnd, hWnd, WM_CHAR, wParam, lParam); ReplaceAddString(); return ret; } }; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { Waifu2x::init_liblary(); // Caffeのエラーでないログを保存しないようにする google::SetLogDestination(google::INFO, ""); google::SetLogDestination(google::WARNING, ""); // Caffeのエラーログを「error_log_〜」に出力 google::SetLogDestination(google::ERROR, "error_log_"); google::SetLogDestination(google::FATAL, "error_log_"); // CDialogクラスでダイアログを作成する CDialog cDialog; CDialog cDialog2; // IDC_EDITのサブクラス CControl cControlInput(IDC_EDIT_INPUT); CControl cControlOutput(IDC_EDIT_OUTPUT); CControl cControlScale(IDC_EDIT_SCALE_RATIO); CControl cControlOutExt(IDC_EDIT_OUT_EXT); // 登録する関数がまとめられたクラス // グローバル関数を使えばクラスにまとめる必要はないがこの方法が役立つこともあるはず DialogEvent cDialogEvent; // クラスの関数を登録する場合 // IDC_EDITにWM_DROPFILESが送られてきたときに実行する関数の登録 cControlInput.SetEventCallBack(SetClassCustomFunc(DialogEvent::DropInput, &cDialogEvent), NULL, WM_DROPFILES); cControlOutput.SetEventCallBack(SetClassCustomFunc(DialogEvent::DropOutput, &cDialogEvent), NULL, WM_DROPFILES); cControlScale.SetEventCallBack(SetClassCustomFunc(DialogEvent::TextInput, &cDialogEvent), NULL, WM_CHAR); cControlOutExt.SetEventCallBack(SetClassCustomFunc(DialogEvent::TextInput, &cDialogEvent), NULL, WM_CHAR); // コントロールのサブクラスを登録 cDialog.AddControl(&cControlInput); cDialog.AddControl(&cControlOutput); cDialog.AddControl(&cControlScale); cDialog.AddControl(&cControlOutExt); // 各コントロールのイベントで実行する関数の登録 cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::Exec, &cDialogEvent), NULL, IDC_BUTTON_EXEC); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::Cancel, &cDialogEvent), NULL, IDC_BUTTON_CANCEL); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODE_NOISE); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODE_SCALE); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODE_NOISE_SCALE); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_AUTO_SCALE); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIONOISE_LEVEL1); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIONOISE_LEVEL2); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODE_CPU); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODE_GPU); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODEL_RGB); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODEL_PHOTO); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_RADIO_MODEL_Y); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::RadioButtom, &cDialogEvent), NULL, IDC_CHECK_TTA); cDialog.SetCommandCallBack(SetClassFunc(DialogEvent::CheckCUDNN, &cDialogEvent), NULL, IDC_BUTTON_CHECK_CUDNN); // ダイアログのイベントで実行する関数の登録 cDialog.SetEventCallBack(SetClassFunc(DialogEvent::Create, &cDialogEvent), NULL, WM_INITDIALOG); cDialog.SetEventCallBack(SetClassFunc(DialogEvent::OnDialogEnd, &cDialogEvent), NULL, WM_CLOSE); cDialog.SetEventCallBack(SetClassFunc(DialogEvent::OnFaildCreateDir, &cDialogEvent), NULL, WM_FAILD_CREATE_DIR); cDialog.SetEventCallBack(SetClassFunc(DialogEvent::OnWaifu2xError, &cDialogEvent), NULL, WM_ON_WAIFU2X_ERROR); cDialog.SetEventCallBack(SetClassFunc(DialogEvent::WaitThreadExit, &cDialogEvent), NULL, WM_END_THREAD); // ダイアログを表示 cDialog.DoModal(hInstance, IDD_DIALOG); Waifu2x::quit_liblary(); return 0; }