358 lines
11 KiB
C++
358 lines
11 KiB
C++
// File: corpus_gen.cpp - Block compression corpus generator.
|
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
|
//
|
|
// Example command line:
|
|
// -gentest [-deep] [-blockpercentage .035] [-width 4096] [-height 4096] -in c:\temp\*.jpg [-in c:\temp\*.jpeg] [-in @blah.txt]
|
|
#include "crn_core.h"
|
|
#include "corpus_gen.h"
|
|
#include "crn_console.h"
|
|
|
|
#include "crn_find_files.h"
|
|
#include "crn_file_utils.h"
|
|
#include "crn_command_line_params.h"
|
|
|
|
#include "crn_dxt.h"
|
|
#include "crn_cfile_stream.h"
|
|
#include "crn_texture_conversion.h"
|
|
#include "crn_radix_sort.h"
|
|
|
|
#define CRND_HEADER_FILE_ONLY
|
|
#include "crn_decomp.h"
|
|
|
|
namespace crnlib
|
|
{
|
|
struct block
|
|
{
|
|
color_quad_u8 m_c[4*4];
|
|
|
|
inline operator size_t() const { return fast_hash(this, sizeof(*this)); }
|
|
|
|
inline bool operator== (const block& rhs) const
|
|
{
|
|
return memcmp(this, &rhs, sizeof(*this)) == 0;
|
|
}
|
|
};
|
|
|
|
typedef crnlib::hash_map<block, empty_type> block_hash_map;
|
|
|
|
corpus_gen::corpus_gen()
|
|
{
|
|
|
|
|
|
}
|
|
|
|
void corpus_gen::sort_blocks(image_u8& img)
|
|
{
|
|
const uint num_blocks_x = img.get_width() / 4;
|
|
const uint num_blocks_y = img.get_height() / 4;
|
|
const uint total_blocks = num_blocks_x * num_blocks_y;
|
|
|
|
console::printf("Sorting %u blocks...", total_blocks);
|
|
|
|
crnlib::vector<float> block_std_dev(total_blocks);
|
|
|
|
for (uint by = 0; by < num_blocks_y; by++)
|
|
{
|
|
for (uint bx = 0; bx < num_blocks_x; bx++)
|
|
{
|
|
color_quad_u8 c[4 * 4];
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
for (uint x = 0; x < 4; x++)
|
|
c[x+y*4] = img(bx*4+x, by*4+y);
|
|
|
|
double std_dev = 0.0f;
|
|
for (uint i = 0; i < 3; i++)
|
|
std_dev += image_utils::compute_std_dev(16, c, i, 1);
|
|
|
|
block_std_dev[bx + by * num_blocks_x] = (float)std_dev;
|
|
}
|
|
}
|
|
|
|
crnlib::vector<uint> block_indices0(total_blocks);
|
|
crnlib::vector<uint> block_indices1(total_blocks);
|
|
|
|
const uint* pIndices = indirect_radix_sort(total_blocks, &block_indices0[0], &block_indices1[0], &block_std_dev[0], 0, sizeof(float), true);
|
|
|
|
image_u8 new_img(img.get_width(), img.get_height());
|
|
|
|
uint dst_block_index = 0;
|
|
|
|
//float prev_std_dev = -999;
|
|
for (uint i = 0; i < total_blocks; i++)
|
|
{
|
|
uint src_block_index = pIndices[i];
|
|
|
|
//float std_dev = block_std_dev[src_block_index];
|
|
//crnlib_ASSERT(std_dev >= prev_std_dev);
|
|
//prev_std_dev = std_dev;
|
|
|
|
uint src_block_x = src_block_index % num_blocks_x;
|
|
uint src_block_y = src_block_index / num_blocks_x;
|
|
|
|
uint dst_block_x = dst_block_index % num_blocks_x;
|
|
uint dst_block_y = dst_block_index / num_blocks_x;
|
|
|
|
new_img.unclipped_blit(src_block_x * 4, src_block_y * 4, 4, 4, dst_block_x * 4, dst_block_y * 4, img);
|
|
|
|
dst_block_index++;
|
|
}
|
|
|
|
#if 0
|
|
//new_img.swap(img);
|
|
#else
|
|
crnlib::vector<uint> remaining_blocks(num_blocks_x);
|
|
|
|
console::printf("Arranging %u blocks...", total_blocks);
|
|
|
|
for (uint by = 0; by < num_blocks_y; by++)
|
|
{
|
|
console::printf("%u of %u", by, num_blocks_y);
|
|
|
|
remaining_blocks.resize(num_blocks_x);
|
|
for (uint i = 0; i < num_blocks_x; i++)
|
|
remaining_blocks[i] = i;
|
|
|
|
color_quad_u8 match_block[16];
|
|
utils::zero_object(match_block);
|
|
for (uint bx = 0; bx < num_blocks_x; bx++)
|
|
{
|
|
uint best_index = 0;
|
|
|
|
uint64 best_error = cUINT64_MAX;
|
|
|
|
for (uint i = 0; i < remaining_blocks.size(); i++)
|
|
{
|
|
uint src_block_index = remaining_blocks[i];
|
|
|
|
uint64 error = 0;
|
|
for (uint y = 0; y < 4; y++)
|
|
{
|
|
for (uint x = 0; x < 4; x++)
|
|
{
|
|
const color_quad_u8& c = new_img(src_block_index*4+x, by*4+y);
|
|
error += color::elucidian_distance(c, match_block[x+y*4], false);
|
|
}
|
|
}
|
|
|
|
if (error < best_error)
|
|
{
|
|
best_error = error;
|
|
best_index = i;
|
|
}
|
|
}
|
|
|
|
uint src_block_index = remaining_blocks[best_index];
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
{
|
|
for (uint x = 0; x < 4; x++)
|
|
{
|
|
const color_quad_u8& c = new_img(src_block_index*4+x, by*4+y);
|
|
match_block[x+y*4] = c;
|
|
|
|
img(bx * 4 + x, by * 4 + y) = c;
|
|
}
|
|
}
|
|
|
|
remaining_blocks.erase_unordered(best_index);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool corpus_gen::generate(const char* pCmd_line)
|
|
{
|
|
static const command_line_params::param_desc param_desc_array[] =
|
|
{
|
|
{ "corpus_gen", 0, false },
|
|
{ "in", 1, true },
|
|
{ "deep", 0, false },
|
|
{ "blockpercentage", 1, false },
|
|
{ "width", 1, false },
|
|
{ "height", 1, false },
|
|
{ "alpha", 0, false },
|
|
};
|
|
|
|
command_line_params params;
|
|
if (!params.parse(pCmd_line, CRNLIB_ARRAY_SIZE(param_desc_array), param_desc_array, true))
|
|
return false;
|
|
|
|
if (!params.has_key("in"))
|
|
{
|
|
console::error("Must specify one or more input files using the /in option!");
|
|
return false;
|
|
}
|
|
|
|
uint num_dst_blocks_x = params.get_value_as_int("width", 0, 4096, 128, 4096);
|
|
num_dst_blocks_x = (num_dst_blocks_x + 3) / 4;
|
|
uint num_dst_blocks_y = params.get_value_as_int("height", 0, 4096, 128, 4096);
|
|
num_dst_blocks_y = (num_dst_blocks_y + 3) / 4;
|
|
|
|
const uint total_dst_blocks = num_dst_blocks_x * num_dst_blocks_y;
|
|
image_u8 dst_img(num_dst_blocks_x * 4, num_dst_blocks_y * 4);
|
|
uint next_dst_block = 0;
|
|
uint total_dst_images = 0;
|
|
|
|
random rm;
|
|
|
|
block_hash_map block_hash;
|
|
block_hash.reserve(total_dst_blocks);
|
|
|
|
uint total_images_loaded = 0;
|
|
uint total_blocks_written = 0;
|
|
|
|
command_line_params::param_map_const_iterator it = params.begin();
|
|
for ( ; it != params.end(); ++it)
|
|
{
|
|
if (it->first != "in")
|
|
continue;
|
|
if (it->second.m_values.empty())
|
|
{
|
|
console::error("Must follow /in parameter with a filename!\n");
|
|
return false;
|
|
}
|
|
|
|
for (uint in_value_index = 0; in_value_index < it->second.m_values.size(); in_value_index++)
|
|
{
|
|
const dynamic_string& filespec = it->second.m_values[in_value_index];
|
|
|
|
find_files file_finder;
|
|
if (!file_finder.find(filespec.get_ptr(), find_files::cFlagAllowFiles | (params.has_key("deep") ? find_files::cFlagRecursive : 0)))
|
|
{
|
|
console::warning("Failed finding files: %s", filespec.get_ptr());
|
|
continue;
|
|
}
|
|
|
|
if (file_finder.get_files().empty())
|
|
{
|
|
console::warning("No files found: %s", filespec.get_ptr());
|
|
return false;
|
|
}
|
|
|
|
const find_files::file_desc_vec& files = file_finder.get_files();
|
|
|
|
for (uint file_index = 0; file_index < files.size(); file_index++)
|
|
{
|
|
const find_files::file_desc& file_desc = files[file_index];
|
|
|
|
console::printf("Loading image: %s", file_desc.m_fullname.get_ptr());
|
|
|
|
image_u8 img;
|
|
if (!image_utils::read_from_file(img, file_desc.m_fullname.get_ptr(), 0))
|
|
{
|
|
console::warning("Failed loading image file: %s", file_desc.m_fullname.get_ptr());
|
|
continue;
|
|
}
|
|
|
|
if (!params.has_key("alpha"))
|
|
{
|
|
for (uint y = 0; y < img.get_height(); y++)
|
|
for (uint x = 0; x < img.get_width(); x++)
|
|
img(x, y).a = 255;
|
|
}
|
|
|
|
total_images_loaded++;
|
|
|
|
uint width = img.get_width();
|
|
uint height = img.get_height();
|
|
|
|
uint num_blocks_x = (width + 3) / 4;
|
|
uint num_blocks_y = (height + 3) / 4;
|
|
uint total_blocks = num_blocks_x * num_blocks_y;
|
|
|
|
float percentage = params.get_value_as_float("blockpercentage", 0, .1f, .001f, 1.0f);
|
|
uint total_rand_blocks = math::maximum<uint>(1U, (uint)(total_blocks * percentage));
|
|
|
|
crnlib::vector<uint> remaining_blocks(total_blocks);
|
|
for (uint i = 0; i < total_blocks; i++)
|
|
remaining_blocks[i] = i;
|
|
|
|
uint num_blocks_remaining = total_rand_blocks;
|
|
while (num_blocks_remaining)
|
|
{
|
|
if (remaining_blocks.empty())
|
|
break;
|
|
|
|
uint rand_block_index = rm.irand(0, remaining_blocks.size());
|
|
uint block_index = remaining_blocks[rand_block_index];
|
|
remaining_blocks.erase_unordered(rand_block_index);
|
|
|
|
uint block_y = block_index / num_blocks_x;
|
|
uint block_x = block_index % num_blocks_x;
|
|
|
|
block b;
|
|
|
|
for (uint y = 0; y < 4; y++)
|
|
{
|
|
for (uint x = 0; x < 4; x++)
|
|
{
|
|
b.m_c[x+y*4] = img.get_clamped(block_x*4+x, block_y*4+y);
|
|
}
|
|
}
|
|
|
|
if (!block_hash.insert(b).second)
|
|
continue;
|
|
if (block_hash.size() == total_dst_blocks)
|
|
{
|
|
block_hash.clear();
|
|
block_hash.reserve(total_dst_blocks);
|
|
}
|
|
|
|
uint dst_block_x = next_dst_block % num_dst_blocks_x;
|
|
uint dst_block_y = next_dst_block / num_dst_blocks_x;
|
|
for (uint y = 0; y < 4; y++)
|
|
{
|
|
for (uint x = 0; x < 4; x++)
|
|
{
|
|
dst_img(dst_block_x * 4 + x, dst_block_y * 4 + y) = b.m_c[x + y*4];
|
|
}
|
|
}
|
|
|
|
next_dst_block++;
|
|
if (total_dst_blocks == next_dst_block)
|
|
{
|
|
sort_blocks(dst_img);
|
|
|
|
dynamic_string dst_filename(cVarArg, "test_%u.tga", total_dst_images);
|
|
console::printf("Writing image: %s", dst_filename.get_ptr());
|
|
image_utils::write_to_file(dst_filename.get_ptr(), dst_img, 0);
|
|
|
|
dst_img.set_all(color_quad_u8::make_black());
|
|
|
|
next_dst_block = 0;
|
|
|
|
total_dst_images++;
|
|
}
|
|
|
|
total_blocks_written++;
|
|
|
|
num_blocks_remaining--;
|
|
}
|
|
|
|
} // file_index
|
|
|
|
} // in_value_index
|
|
|
|
}
|
|
|
|
if (next_dst_block)
|
|
{
|
|
sort_blocks(dst_img);
|
|
|
|
dynamic_string dst_filename(cVarArg, "test_%u.tga", total_dst_images);
|
|
console::printf("Writing image: %s", dst_filename.get_ptr());
|
|
image_utils::write_to_file(dst_filename.get_ptr(), dst_img, 0);
|
|
|
|
next_dst_block = 0;
|
|
|
|
total_dst_images++;
|
|
}
|
|
|
|
console::printf("Found %u input images, %u output images, %u total blocks", total_images_loaded, total_dst_images, total_blocks_written);
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace crnlib
|