bool saveImageData(const char* pFilename, ImageData& id) { fipImage dst_image(FIT_BITMAP, (WORD)id.width, (WORD)id.height, 32); for (uint y = 0; y < id.height; y++) { int idx = y* id.width * 4; for (uint x = 0; x < id.width; x++, idx+=4) { RGBAColor c; RGBQUAD quad; quad.rgbRed = id.pImgData[idx +0]; quad.rgbGreen = id.pImgData[idx +1]; quad.rgbBlue = id.pImgData[idx +2]; quad.rgbReserved = id.pImgData[idx +3]; dst_image.setPixelColor(x, id.height - 1 - y, &quad); } } if (!dst_image.save(pFilename, 0)) return false; return true; }
int normal_process(const char* argv[]) { const std::string src_path {argv[1]}; const std::string dst_path {argv[2]}; const int dst_width {atoi(argv[3])}; const int dst_height {atoi(argv[4])}; // load a source image const cv::Mat src_image {cv::imread(src_path)}; if (src_image.empty()) { std::cout << "file not found\n"; return 1; } std::cout << boost::format("(src_width, src_height) = (%1%, %2%)") % src_image.cols % src_image.rows << std::endl; // make buffer for output image cv::Mat dst_image(dst_height, dst_width, CV_8UC3); std::cout << boost::format("(dst_width, dst_height) = (%1%, %2%)") % dst_image.cols % dst_image.rows << std::endl; std::cout << std::endl; // use raw pointer constexpr int NUM {10}; for (auto i = 0; i < NUM; ++i) { auto start {std::chrono::system_clock::now()}; resize_with_raw_access(src_image, dst_image); auto end {std::chrono::system_clock::now()}; std::cout << boost::format("raw access[%1%]: %2% msec\n") % i % std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); } std::cout << std::endl; // save the dst image cv::imwrite(dst_path, dst_image); return 0; }
int main(int arg_c, char** arg_v) { if (arg_c != 5) { printf("Usage: input_image output_image.tga width height\n"); return EXIT_FAILURE; } const char* pSrc_filename = arg_v[1]; const char* pDst_filename = arg_v[2]; const int dst_width = atoi(arg_v[3]); const int dst_height = atoi(arg_v[4]); if ((std::min(dst_width, dst_height) < 1) || (std::max(dst_width, dst_height) > RESAMPLER_MAX_DIMENSION)) { printf("Invalid output width/height!\n"); return EXIT_FAILURE; } printf("Loading image: %s\n", pSrc_filename); int src_width, src_height, n; unsigned char* pSrc_image = stbi_load(pSrc_filename, &src_width, &src_height, &n, 0); if (!pSrc_image) { printf("Failed loading image!\n"); return EXIT_FAILURE; } printf("Resolution: %ux%u, Channels: %u\n", src_width, src_height, n); const int max_components = 4; if ((std::max(src_width, src_height) > RESAMPLER_MAX_DIMENSION) || (n > max_components)) { printf("Image is too large!\n"); return EXIT_FAILURE; } // Partial gamma correction looks better on mips. Set to 1.0 to disable gamma correction. const float source_gamma = 1.75f; // Filter scale - values < 1.0 cause aliasing, but create sharper looking mips. const float filter_scale = 1.0f;//.75f; const char* pFilter = "blackman";//RESAMPLER_DEFAULT_FILTER; float srgb_to_linear[256]; for (int i = 0; i < 256; ++i) srgb_to_linear[i] = (float)pow(i * 1.0f/255.0f, source_gamma); const int linear_to_srgb_table_size = 4096; unsigned char linear_to_srgb[linear_to_srgb_table_size]; const float inv_linear_to_srgb_table_size = 1.0f / linear_to_srgb_table_size; const float inv_source_gamma = 1.0f / source_gamma; for (int i = 0; i < linear_to_srgb_table_size; ++i) { int k = (int)(255.0f * pow(i * inv_linear_to_srgb_table_size, inv_source_gamma) + .5f); if (k < 0) k = 0; else if (k > 255) k = 255; linear_to_srgb[i] = (unsigned char)k; } Resampler* resamplers[max_components]; std::vector<float> samples[max_components]; // Now create a Resampler instance for each component to process. The first instance will create new contributor tables, which are shared by the resamplers // used for the other components (a memory and slight cache efficiency optimization). resamplers[0] = new Resampler(src_width, src_height, dst_width, dst_height, Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f, pFilter, NULL, NULL, filter_scale, filter_scale); samples[0].resize(src_width); for (int i = 1; i < n; i++) { resamplers[i] = new Resampler(src_width, src_height, dst_width, dst_height, Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f, pFilter, resamplers[0]->get_clist_x(), resamplers[0]->get_clist_y(), filter_scale, filter_scale); samples[i].resize(src_width); } std::vector<unsigned char> dst_image(dst_width * n * dst_height); const int src_pitch = src_width * n; const int dst_pitch = dst_width * n; int dst_y = 0; printf("Resampling to %ux%u\n", dst_width, dst_height); for (int src_y = 0; src_y < src_height; src_y++) { const unsigned char* pSrc = &pSrc_image[src_y * src_pitch]; for (int x = 0; x < src_width; x++) { for (int c = 0; c < n; c++) { if ((c == 3) || ((n == 2) && (c == 1))) samples[c][x] = *pSrc++ * (1.0f/255.0f); else samples[c][x] = srgb_to_linear[*pSrc++]; } } for (int c = 0; c < n; c++) { if (!resamplers[c]->put_line(&samples[c][0])) { printf("Out of memory!\n"); return EXIT_FAILURE; } } for ( ; ; ) { int comp_index; for (comp_index = 0; comp_index < n; comp_index++) { const float* pOutput_samples = resamplers[comp_index]->get_line(); if (!pOutput_samples) break; const bool alpha_channel = (comp_index == 3) || ((n == 2) && (comp_index == 1)); assert(dst_y < dst_height); unsigned char* pDst = &dst_image[dst_y * dst_pitch + comp_index]; for (int x = 0; x < dst_width; x++) { if (alpha_channel) { int c = (int)(255.0f * pOutput_samples[x] + .5f); if (c < 0) c = 0; else if (c > 255) c = 255; *pDst = (unsigned char)c; } else { int j = (int)(linear_to_srgb_table_size * pOutput_samples[x] + .5f); if (j < 0) j = 0; else if (j >= linear_to_srgb_table_size) j = linear_to_srgb_table_size - 1; *pDst = linear_to_srgb[j]; } pDst += n; } } if (comp_index < n) break; dst_y++; } } printf("Writing TGA file: %s\n", pDst_filename); if (!stbi_write_tga(pDst_filename, dst_width, dst_height, n, &dst_image[0])) { printf("Failed writing output image!\n"); return EXIT_FAILURE; } stbi_image_free(pSrc_image); // Delete the resamplers. for (int i = 0; i < n; i++) delete resamplers[i]; return EXIT_SUCCESS; }