diff --git a/src/convertRoutine.cpp b/src/convertRoutine.cpp index 3f89a770..a6309456 100644 --- a/src/convertRoutine.cpp +++ b/src/convertRoutine.cpp @@ -348,6 +348,10 @@ static bool convertWithModelsBlockSplit(W2XConv *conv, } else { clipEndY = r * clipHeight + blockHeight; } + + if (enableLog) { + printf("Processing block, row (%02d/%02d) ...\n", (r+1), splitRows); + } for (unsigned int c = 0; c < splitColumns; c++) { // start to convert diff --git a/src/main.cpp b/src/main.cpp index c3e937f9..f108592f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -458,10 +458,14 @@ std::wstring generate_output_location(std::wstring inputFileName, std::wstring o } #endif - void convert_file(ConvInfo info, fs::path inputName, fs::path output) { //std::cout << "Operating on: " << fs::absolute(inputName).string() << std::endl; + +#if defined(WIN32) && defined(UNICODE) + std::wstring outputName = generate_output_location(fs::absolute(inputName).wstring(), output.wstring(), info.mode, info.NRLevel, info.scaleRatio, info.outputFormat); +#else std::string outputName = generate_output_location(fs::absolute(inputName).string(), output.string(), info.mode, info.NRLevel, info.scaleRatio, info.outputFormat); +#endif int _nrLevel = -1; @@ -476,35 +480,11 @@ void convert_file(ConvInfo info, fs::path inputName, fs::path output) { int error = w2xconv_convert_file(info.converter, outputName.c_str(), - fs::absolute(inputName).string().c_str(), - _nrLevel, - _scaleRatio, - info.blockSize, - info.imwrite_params - ); - - check_for_errors(info.converter, error); -} - #if defined(WIN32) && defined(UNICODE) -void convert_fileW(ConvInfo info, fs::path inputName, fs::path output) { - //std::cout << "Operating on: " << fs::absolute(inputName).string() << std::endl; - std::wstring outputName = generate_output_location(fs::absolute(inputName).wstring(), output.wstring(), info.mode, info.NRLevel, info.scaleRatio, info.outputFormat); - - int _nrLevel = -1; - - if (strcmp(info.mode.c_str(), "noise")==0 || strcmp(info.mode.c_str(), "noise-scale")==0) { - _nrLevel = info.NRLevel; - } - - double _scaleRatio = 1; - if (strcmp(info.mode.c_str(), "scale")==0 || strcmp(info.mode.c_str(), "noise-scale")==0) { - _scaleRatio = info.scaleRatio; - } - - int error = w2xconv_convert_fileW(info.converter, - outputName.c_str(), fs::absolute(inputName).wstring().c_str(), +#else + fs::absolute(inputName).string().c_str(), +#endif _nrLevel, _scaleRatio, info.blockSize, @@ -513,7 +493,6 @@ void convert_fileW(ConvInfo info, fs::path inputName, fs::path output) { check_for_errors(info.converter, error); } -#endif @@ -733,6 +712,13 @@ int main(int argc, char** argv) std::exit(-1); } + //Check scale-ratio is vaild. + if (cmdScaleRatio.getValue() < 0 || cmdScaleRatio.getValue() > 1024) + { + std::cout << "Error: Scale Ratio range is 0-1024" << std::endl; + std::exit(-1); + } + //Check Quality/Compression option ranges. if (cmdPngCompression.getValue() < 0 || cmdPngCompression.getValue() > 9) { @@ -878,11 +864,7 @@ int main(int argc, char** argv) ); try { -#if defined(WIN32) && defined(UNICODE) - convert_fileW(convInfo, fn, output); -#else convert_file(convInfo, fn, output); -#endif } catch (const std::exception& e) { numErrors++; @@ -916,11 +898,7 @@ int main(int argc, char** argv) double time_file_start = getsec(); std::cout << "Processing file [1/1] \"" << input << "\":" << (verbose ? "\n" : " "); try { -#if defined(WIN32) && defined(UNICODE) - convert_fileW(convInfo, input, output); -#else convert_file(convInfo, input, output); -#endif } catch (const std::exception& e) { numErrors++; diff --git a/src/w2xconv.cpp b/src/w2xconv.cpp index b1d685f3..b29cde33 100644 --- a/src/w2xconv.cpp +++ b/src/w2xconv.cpp @@ -498,6 +498,14 @@ w2xconv_strerror(W2XConvError *e) case W2XCONV_ERROR_Y_MODEL_MISMATCH_TO_RGB_F32: oss << "cannot apply y model to rgb_f32."; break; + + case W2XCONV_ERROR_SCALE_LIMIT: + oss << "image scale is too big to convert."; + break; + + case W2XCONV_ERROR_SIZE_LIMIT: + oss << "image width (or height) under 40px cannot converted in this scale."; + break; } return strdup(oss.str().c_str()); @@ -1448,89 +1456,6 @@ w2xconv_convert_mat(struct W2XConv *conv, } } -int w2xconv_convert_file( - struct W2XConv *conv, - const char *dst_path, - const char *src_path, - int denoise_level, - double scale, - int blockSize, - int* imwrite_params) -{ - double time_start = getsec(); - - FILE *png_fp = nullptr; - png_fp = fopen(src_path, "rb"); - - if (png_fp == nullptr) { - setPathError(conv, W2XCONV_ERROR_IMREAD_FAILED, src_path); - return -1; - } - - bool png_rgb; - //Background colour - //float3 background(1.0f, 1.0f, 1.0f); - w2xconv_rgb_float3 background; - background.r = background.g = background.b = 1.0f; - get_png_background_colour(png_fp, &png_rgb, &background); - - if (png_fp) { - fclose(png_fp); - png_fp = nullptr; - } - - cv::Mat image_src, image_dst; - - /* - * IMREAD_COLOR : always BGR - * IMREAD_UNCHANGED + png : BGR or BGRA - * IMREAD_UNCHANGED + otherwise : ??? - */ - if (png_rgb) { - image_src = cv::imread(src_path, cv::IMREAD_UNCHANGED); - } else { - image_src = cv::imread(src_path, cv::IMREAD_COLOR); - } - - bool dst_png = false; - { - size_t len = strlen(dst_path); - if (len >= 4) { - if (tolower(dst_path[len-4]) == '.' && - tolower(dst_path[len-3]) == 'p' && - tolower(dst_path[len-2]) == 'n' && - tolower(dst_path[len-1]) == 'g') - { - dst_png = true; - } - } - } - - w2xconv_convert_mat(conv, image_dst, image_src, denoise_level, scale, blockSize, background, png_rgb, dst_png); - - std::vector compression_params; - for ( int i = 0; i < sizeof(imwrite_params); i = i + 1 ) - { - compression_params.push_back(imwrite_params[i]); - } - - if (!cv::imwrite(dst_path, image_dst, compression_params)) { - setPathError(conv, - W2XCONV_ERROR_IMWRITE_FAILED, - dst_path); - return -1; - } - - double time_end = getsec(); - - conv->flops.process_sec += time_end - time_start; - - //printf("== %f == \n", conv->impl->env.transfer_wait); - - return 0; -} - - #if defined(WIN32) && defined(UNICODE) void read_imageW(cv::Mat* image, const WCHAR* filepath, int flags=cv::IMREAD_COLOR) { @@ -1562,7 +1487,7 @@ void read_imageW(cv::Mat* image, const WCHAR* filepath, int flags=cv::IMREAD_COL delete[] imgBuffer; } -bool write_imageW(const WCHAR* filepath, cv::Mat& img, int* param) +bool write_imageW(const WCHAR* filepath, cv::Mat& img, std::vector& param) { FILE* pFile; std::vector imageBuffer; @@ -1571,12 +1496,7 @@ bool write_imageW(const WCHAR* filepath, cv::Mat& img, int* param) ext_w = ext_w.substr(ext_w.find_last_of(L'.')); ext.assign(ext_w.begin(), ext_w.end()); - std::vector compression_params; - for ( int i = 0; i < sizeof(param); i = i + 1 ) - { - compression_params.push_back(param[i]); - } - if(!cv::imencode(ext.c_str(),img, imageBuffer, compression_params) ) + if(!cv::imencode(ext.c_str(),img, imageBuffer, param) ) return false; pFile = _wfopen(filepath, L"wb+"); @@ -1588,11 +1508,17 @@ bool write_imageW(const WCHAR* filepath, cv::Mat& img, int* param) fclose(pFile); return true; } +#endif -int w2xconv_convert_fileW( +int w2xconv_convert_file( struct W2XConv *conv, +#if defined(WIN32) && defined(UNICODE) const WCHAR *dst_path, const WCHAR *src_path, +#else + const char *dst_path, + const char *src_path, +#endif int denoise_level, double scale, int blockSize, @@ -1601,7 +1527,12 @@ int w2xconv_convert_fileW( double time_start = getsec(); FILE *png_fp = nullptr; + +#if defined(WIN32) && defined(UNICODE) png_fp = _wfopen(src_path, L"rb"); +#else + png_fp = fopen(src_path, "rb"); +#endif if (png_fp == nullptr) { setPathError(conv, W2XCONV_ERROR_IMREAD_FAILED, src_path); @@ -1627,12 +1558,14 @@ int w2xconv_convert_fileW( * IMREAD_UNCHANGED + png : BGR or BGRA * IMREAD_UNCHANGED + otherwise : ??? */ + +#if defined(WIN32) && defined(UNICODE) if (png_rgb) { read_imageW(&image_src, src_path, cv::IMREAD_UNCHANGED); } else { read_imageW(&image_src, src_path, cv::IMREAD_COLOR); } - + bool dst_png = false; { size_t len = wcslen(dst_path); @@ -1646,10 +1579,205 @@ int w2xconv_convert_fileW( } } } +#else + if (png_rgb) { + image_src = cv::imread(src_path, cv::IMREAD_UNCHANGED); + } else { + image_src = cv::imread(src_path, cv::IMREAD_COLOR); + } - w2xconv_convert_mat(conv, image_dst, image_src, denoise_level, scale, blockSize, background, png_rgb, dst_png); - - if (!write_imageW(dst_path, image_dst, imwrite_params)) { + bool dst_png = false; + { + size_t len = strlen(dst_path); + if (len >= 4) { + if (tolower(dst_path[len-4]) == '.' && + tolower(dst_path[len-3]) == 'p' && + tolower(dst_path[len-2]) == 'n' && + tolower(dst_path[len-1]) == 'g') + { + dst_png = true; + } + } + } +#endif + + //pieces.push_back(image_src); + //image_src.release(); // push_back does deep copy + + const static int pad = 12; // give pad to avoid distortions in edge + + // w2x converts 2x and down scales when scale_ratio is not power of 2 (ex: 2.28 -> scale x4 - > down scale) + int max_scale = static_cast(std::pow(2, std::ceil(std::log2(scale)))); + + //printf("max_scale: %d\n", max_scale); + //char name[70]=""; // for imwrite test + + // comment is for slicer function + // output file pixel above 178,756,920px is limit. leave 56,920px for safe conversion. see issue #156 + // all images that needs slices, it will require 20 px padding to 2 edges (input should w > 40, h > 40). + // with max_scale is 2, it only can converts less then (w+20) x (h+20) = 44,675,000 px. + // with max_scale is 4, it only can converts less then (w+20) x (h+20) = 11,168,750 px. + // with max_scale is 8, it only can converts less then (w+20) x (h+20) = 2,792,187 px. + // with max_scale is 16, it only can converts less then (w+20) x (h+20) = 698,046 px. + // with max_scale is 32, it only can converts less then (w+20) x (h+20) = 174,511 px. + // with max_scale is 64, it only can converts less then (w+20) x (h+20) = 3,627 px. + // with max_scale is 128, it only can converts less then (w+20) x (h+20) = 10,906 px. + // with max_scale is 256, it only can converts less then (w+20) x (h+20) = 2,726 px. + // with max_scale is 512, it only can converts less then (w+20) x (h+20) = 681 px. padding is all most eat everything (pieces are under 6px) + // with max_scale is 1024, it only can converts less then (w+20) x (h+20) = 170 px, padding exceed limit (20 x 20 = 400). + // with max_scale is 2048, it only can converts less then (w+20) x (h+20) = 42 px, which is no meaning to run w2x. + // with max_scale is 4096, you cannot convert it at all. + + if( image_src.rows * image_src.cols > 178700000 / 4 ){ + if( max_scale >= 512 ){ + setError(conv, W2XCONV_ERROR_SCALE_LIMIT); + return -1; + } + else if( ( image_src.rows < 40 || image_src.cols < 40 ) && image_src.rows * image_src.cols > 178700000 / 4){ + setError(conv, W2XCONV_ERROR_SIZE_LIMIT); + return -1; + } + } + + if(denoise_level != -1) + { + if (conv->enable_log) { + printf("\nDenoise before Proccessing...\n"); + } + w2xconv_convert_mat(conv, image_src, image_src, denoise_level, 1, blockSize, background, png_rgb, dst_png); + } + + int iteration_2x = static_cast(std::ceil(std::log2(scale))) ; + double shrinkRatio = scale / std::pow(2.0, static_cast(iteration_2x)); + + image_src.copyTo(image_dst); + image_src.release(); + + for( int ld = 0; ld < iteration_2x ; ld++) + { + // divide images in to 4^n pieces when output width is bigger then 8000^2.... + std::vector pieces, converted; + + if (conv->enable_log) { + printf("\nProccessing [%d/%d] steps...\n", ld+1, iteration_2x); + } + + pieces.push_back(image_dst); + + while( pieces.front().rows * pieces.front().cols > 178700000 / 4 ) + { + cv::Mat front = pieces.front(); + int r=front.rows, c=front.cols; + int h_r=r/2, h_c=c/2; + + // div in 4 and add padding to input. + pieces.push_back(front(cv::Range(0,h_r+pad), cv::Range(0,h_c+pad))); + pieces.push_back(front(cv::Range(0,h_r+pad), cv::Range(h_c-pad,c))); + pieces.push_back(front(cv::Range(h_r-pad,r), cv::Range(0,h_c+pad))); + pieces.push_back(front(cv::Range(h_r-pad,r), cv::Range(h_c-pad,c))); + + // delete piece + pieces.erase(pieces.begin()); + } + + for( int i=0; ienable_log) { + printf("\nProccessing [%d/%zu] slices\n", i+1, pieces.size()); + } + + w2xconv_convert_mat(conv, res, pieces.at(i), -1, 2, blockSize, background, png_rgb, dst_png); + + converted.push_back(res); + + // pieces.erase(pieces.begin()); // not needed. w2xconv_convert_mat will automatically release memory of input mat. + + /* + sprintf(name, "[test] step%d_slice%d_converted.png", ld, i); + cv::imwrite(name, res); + */ + } + + int j=0; // for test_merge + + // combine images + while (converted.size() > 1) + { + cv::Mat quarter[4], tmp, merged; + int cut = (int) (pad * 2); + + if (conv->enable_log) { + printf("\nMerging slices back to one image... in queue: %zd slices\n", converted.size()); + } + + //double time_a = getsec(), time_b = 0; + + tmp=converted.at(0)(cv::Range(0, converted.at(0).rows - cut), cv::Range(0, converted.at(0).cols - cut)); + tmp.copyTo(quarter[0]); + tmp=converted.at(1)(cv::Range(0, converted.at(1).rows - cut), cv::Range(cut, converted.at(1).cols)); + tmp.copyTo(quarter[1]); + tmp=converted.at(2)(cv::Range(cut, converted.at(2).rows), cv::Range(0, converted.at(2).cols - cut)); + tmp.copyTo(quarter[2]); + tmp=converted.at(3)(cv::Range(cut, converted.at(3).rows), cv::Range(cut, converted.at(3).cols)); + tmp.copyTo(quarter[3]); + + converted.erase(converted.begin(), converted.begin()+4); + + //printf("merge horizon\n"); + hconcat(quarter[0], quarter[1], quarter[0]); + hconcat(quarter[2], quarter[3], quarter[2]); + + //printf("merge vertical\n"); + vconcat(quarter[0], quarter[2], merged); + + //time_b = getsec(); + //printf("took %f\n", time_b - time_a); + + converted.push_back(merged); + + /* + printf("imwriting merged image\n"); + sprintf(name, "[test] merge_step%d_block%d.png", ld, j++); + cv::imwrite(name, merged);*/ + } + image_dst = converted.front(); + } + + if (shrinkRatio != 0.0) { + if (conv->enable_log) { + printf("\nResizing image to input scale...\n"); + } + cv::Size lastImageSize = image_dst.size(); + lastImageSize.width = + static_cast(static_cast(lastImageSize.width + * shrinkRatio)); + lastImageSize.height = + static_cast(static_cast(lastImageSize.height + * shrinkRatio)); + cv::resize(image_dst, image_dst, lastImageSize, 0, 0, cv::INTER_LINEAR); + } + + if (conv->enable_log) { + printf("Writing image to file...\n\n"); + } + + std::vector compression_params; + for ( int i = 0; i < sizeof(imwrite_params); i = i + 1 ) + { + compression_params.push_back(imwrite_params[i]); + } + +#if defined(WIN32) && defined(UNICODE) + if (!write_imageW(dst_path, image_dst, compression_params)) { +#else + if (!cv::imwrite(dst_path, image_dst, compression_params)) { +#endif setPathError(conv, W2XCONV_ERROR_IMWRITE_FAILED, dst_path); @@ -1664,7 +1792,6 @@ int w2xconv_convert_fileW( return 0; } -#endif static void diff --git a/src/w2xconv.h b/src/w2xconv.h index 33d5f7ac..5d9a0bd8 100644 --- a/src/w2xconv.h +++ b/src/w2xconv.h @@ -53,6 +53,9 @@ enum W2XConvErrorCode { W2XCONV_ERROR_Y_MODEL_MISMATCH_TO_RGB_F32, W2XCONV_ERROR_OPENCL, /* u.cl_error */ + + W2XCONV_ERROR_SCALE_LIMIT, + W2XCONV_ERROR_SIZE_LIMIT, }; struct W2XConvError { @@ -195,22 +198,17 @@ W2XCONV_EXPORT void w2xconv_fini(struct W2XConv *conv); W2XCONV_EXPORT int w2xconv_convert_file(struct W2XConv *conv, - const char *dst_path, - const char *src_path, - int denoise_level, /* -1:none, 0:L0 denoise, 1:L1 denoise, 2:L2 denoise, 3:L3 denoise */ - double scale, - int block_size, - int* imwrite_params); - #if defined(WIN32) && defined(UNICODE) -W2XCONV_EXPORT int w2xconv_convert_fileW(struct W2XConv *conv, const WCHAR *dst_path, const WCHAR *src_path, +#else + const char *dst_path, + const char *src_path, +#endif int denoise_level, /* -1:none, 0:L0 denoise, 1:L1 denoise, 2:L2 denoise, 3:L3 denoise */ double scale, int block_size, int* imwrite_params); -#endif W2XCONV_EXPORT int w2xconv_convert_rgb(struct W2XConv *conv, unsigned char *dst, size_t dst_step_byte, /* rgb24 (src_w*ratio, src_h*ratio) */ diff --git a/w32-apps/w2xcr.c b/w32-apps/w2xcr.c index d188400b..c90e370e 100644 --- a/w32-apps/w2xcr.c +++ b/w32-apps/w2xcr.c @@ -400,7 +400,7 @@ proc_thread(void *ap) imwrite_params[5] = 5; p = &app->path_list[li]; - r = w2xconv_convert_fileW(c, + r = w2xconv_convert_file(c, p->dst_path, p->src_path, p->denoise,