Fix tiled rotation.
- Add TiledImage::access_pixel(). - Add TiledImage::print_tile(). - Compare implementations.
This commit is contained in:
parent
007ad2284e
commit
7e33e85909
255
rotation.cpp
255
rotation.cpp
|
@ -250,6 +250,59 @@ struct TiledImage : public Image {
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
access_pixel(unsigned int x, unsigned int y)
|
||||
{
|
||||
if (x >= width || y >= height)
|
||||
return nullptr;
|
||||
|
||||
unsigned int const tile_width = tile_w * 3;
|
||||
|
||||
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
|
||||
uint8_t* tile = tiles[tile_index];
|
||||
unsigned int const tile_j = y % tile_h;
|
||||
unsigned int const tile_i = x % tile_w;
|
||||
return tile + tile_j * tile_width + (tile_i * 3);
|
||||
}
|
||||
|
||||
uint8_t const*
|
||||
access_pixel(unsigned int x, unsigned int y) const
|
||||
{
|
||||
if (x >= width || y >= height)
|
||||
return nullptr;
|
||||
|
||||
unsigned int const tile_width = tile_w * 3;
|
||||
|
||||
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
|
||||
//cout << "tile index: " << tile_index << endl;
|
||||
uint8_t* tile = tiles[tile_index];
|
||||
unsigned int const tile_j = y % tile_h;
|
||||
unsigned int const tile_i = x % tile_w;
|
||||
return tile + tile_j * tile_width + (tile_i * 3);
|
||||
}
|
||||
|
||||
void
|
||||
print_tile(unsigned int index) const
|
||||
{
|
||||
cout << "Tile[" << index << "]" << endl;
|
||||
uint8_t const* tile = tiles[index];
|
||||
unsigned int const tile_width = tile_w * 3;
|
||||
for (unsigned int j = 0; j < tile_h; ++j)
|
||||
{
|
||||
for (unsigned int i = 0; i < tile_w; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
cout << ", ";
|
||||
uint8_t const* p = tile + j * tile_width + i * 3;
|
||||
cout << (int) *p << " " << (int) *(p + 1) << " " << (int) *(p + 2);
|
||||
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
void allocate_memory(unsigned int w, unsigned int h)
|
||||
{
|
||||
|
@ -277,19 +330,11 @@ struct TiledImage : public Image {
|
|||
{
|
||||
this->allocate_memory(width, height);
|
||||
|
||||
unsigned int const tile_width = tile_w * 3;
|
||||
|
||||
// Pixel loading
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
{
|
||||
unsigned int const tile_index = (j / tile_h) * nb_col_tile + (i / tile_w);
|
||||
uint8_t* tile = tiles[tile_index];
|
||||
unsigned int const tile_j = j % tile_h;
|
||||
unsigned int const tile_i = i % tile_w;
|
||||
// if (i % 3 == 0)
|
||||
// cout << "pixel (" << i / 3 << ", " << j << ") goes in tile (" << i / (tile_w * 3) << ", " << j / tile_h << ")" << endl;
|
||||
tile += tile_j * tile_width + (tile_i * 3);
|
||||
uint8_t* tile = this->access_pixel(i, j);
|
||||
*(tile++) = istr.get();
|
||||
*(tile++) = istr.get();
|
||||
*(tile++) = istr.get();
|
||||
|
@ -300,16 +345,10 @@ struct TiledImage : public Image {
|
|||
|
||||
virtual bool write_body(std::ofstream& ostr) const override
|
||||
{
|
||||
unsigned int const tile_width = tile_w * 3;
|
||||
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
{
|
||||
unsigned int const tile_index = (j / tile_h) * nb_col_tile + (i / tile_w);
|
||||
uint8_t* tile = tiles[tile_index];
|
||||
unsigned int const tile_j = j % tile_h;
|
||||
unsigned int const tile_i = i % tile_w;
|
||||
tile += tile_j * tile_width + (tile_i * 3);
|
||||
uint8_t const* tile = this->access_pixel(i, j);
|
||||
ostr << (char) *(tile++);
|
||||
ostr << (char) *(tile++);
|
||||
ostr << (char) *(tile++);
|
||||
|
@ -532,6 +571,9 @@ void round_if_very_small(double& d)
|
|||
{
|
||||
if (abs(d) < 1.0e-10)
|
||||
d = 0.0;
|
||||
|
||||
if (abs(d - 1) < 1.0e-10)
|
||||
d = 1.0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -576,6 +618,13 @@ void rotate_pixel(Image const& src, Image& rotated,
|
|||
double y_delta = src_rotated_point.y - floor(src_rotated_point.y);
|
||||
round_if_very_small(y_delta);
|
||||
|
||||
// special case if we can directly map the src to the dest
|
||||
if (x_delta == 0 && y_delta == 0)
|
||||
{
|
||||
memcpy(&rotated.buffer[rot_index], &src.buffer[src_index], 3 * sizeof (uint8_t));
|
||||
return;
|
||||
}
|
||||
|
||||
// SIMD
|
||||
__m128 const x_d = _mm_set_ps1(x_delta);
|
||||
__m128 const inv_x_d = _mm_set_ps1(1 - x_delta);
|
||||
|
@ -692,6 +741,136 @@ Image rotate(Image const& src, double angle)
|
|||
return rotated;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Tile rotation
|
||||
//
|
||||
|
||||
template<unsigned int W, unsigned int H>
|
||||
void rotate_pixel(TiledImage<W, H> const& src, TiledImage<W, H>& rotated,
|
||||
DPoint const& src_rotated_point,
|
||||
unsigned int rot_tile_index, unsigned int rot_index)
|
||||
{
|
||||
uint8_t const* src_index_1 = src.access_pixel((int) src_rotated_point.x, (int) src_rotated_point.y);
|
||||
|
||||
double x_delta = src_rotated_point.x - (int) src_rotated_point.x;
|
||||
round_if_very_small(x_delta);
|
||||
double y_delta = src_rotated_point.y - (int) src_rotated_point.y;
|
||||
round_if_very_small(y_delta);
|
||||
|
||||
// special case if we can directly map the src to the dest
|
||||
if (x_delta == 0 && y_delta == 0)
|
||||
{
|
||||
// cout << "we can directly map, w00t" << endl;
|
||||
uint8_t* rot_tile = rotated.tiles[rot_tile_index];
|
||||
memcpy(&rot_tile[rot_index], src_index_1, 3 * sizeof (uint8_t));
|
||||
return;
|
||||
}
|
||||
// cout << "src rotated point: " << src_rotated_point << endl;
|
||||
// cout << "src rotated point y: " << src_rotated_point.y << endl;
|
||||
// cout << "src rotated point y int: " << (int) src_rotated_point.y << endl;
|
||||
// cout << "x delta = " << x_delta << endl;
|
||||
// cout << "y delta = " << y_delta << endl;
|
||||
|
||||
uint8_t const* src_index_2 = src.access_pixel((int) src_rotated_point.x + 1, (int) src_rotated_point.y);
|
||||
uint8_t const* src_index_3 = src.access_pixel((int) src_rotated_point.x, (int) src_rotated_point.y + 1);
|
||||
uint8_t const* src_index_4 = src.access_pixel((int) src_rotated_point.x + 1, (int) src_rotated_point.y + 1);
|
||||
|
||||
// FIXME: deal with image border
|
||||
if (!src_index_1 || !src_index_2 || !src_index_3 || !src_index_4)
|
||||
return;
|
||||
|
||||
// SIMD
|
||||
__m128 const x_d = _mm_set_ps1(x_delta);
|
||||
__m128 const inv_x_d = _mm_set_ps1(1 - x_delta);
|
||||
__m128 top_left = _mm_set_ps(*src_index_1, *(src_index_1 + 1), *(src_index_1 + 2), 0.0);
|
||||
__m128 top_right = _mm_set_ps(*src_index_2, *(src_index_2 + 1), *(src_index_2 + 2), 0.0);
|
||||
top_left = _mm_mul_ps(top_left, inv_x_d);
|
||||
top_right = _mm_mul_ps(top_right, x_d);
|
||||
top_left = _mm_add_ps(top_left, top_right);
|
||||
|
||||
__m128 bottom_left = _mm_set_ps(*src_index_3, *(src_index_3 + 1), *(src_index_3 + 2), 0.0);
|
||||
__m128 bottom_right = _mm_set_ps(*src_index_4, *(src_index_4 + 1), *(src_index_4 + 2), 0.0);
|
||||
bottom_left = _mm_mul_ps(bottom_left, inv_x_d);
|
||||
bottom_right = _mm_mul_ps(bottom_right, x_d);
|
||||
bottom_left = _mm_add_ps(bottom_left, bottom_right);
|
||||
|
||||
__m128 const y_d = _mm_set_ps1(y_delta);
|
||||
__m128 const inv_y_d = _mm_set_ps1(1 - y_delta);
|
||||
top_left = _mm_mul_ps(top_left, inv_y_d);
|
||||
bottom_left = _mm_mul_ps(bottom_left, y_d);
|
||||
top_left = _mm_add_ps(top_left, bottom_left);
|
||||
|
||||
// convert float values to uint8_t
|
||||
uint8_t* rot_tile = rotated.tiles[rot_tile_index];
|
||||
rot_tile[rot_index] = top_left[3];
|
||||
rot_tile[rot_index + 1] = top_left[2];
|
||||
rot_tile[rot_index + 2] = top_left[1];
|
||||
}
|
||||
|
||||
template<unsigned int W, unsigned int H>
|
||||
TiledImage<W, H> rotate(TiledImage<W, H> const& src, double angle)
|
||||
{
|
||||
double const rotation = (angle / 180.0f) * M_PI;
|
||||
unsigned int w = 0;
|
||||
unsigned int h = 0;
|
||||
compute_output_size(src, rotation, w, h);
|
||||
TiledImage<W, H> rotated(w, h);
|
||||
|
||||
DPoint src_origin = get_mapped_point(rotated, Point(0, 0), -rotation);
|
||||
DPoint src_delta_x = get_mapped_point(rotated, Point(1, 0), -rotation);
|
||||
DPoint src_delta_y = get_mapped_point(rotated, Point(0, 1), -rotation);
|
||||
|
||||
src_delta_x.x = src_delta_x.x - src_origin.x;
|
||||
src_delta_x.y = src_delta_x.y - src_origin.y;
|
||||
round_if_very_small(src_delta_x.x);
|
||||
round_if_very_small(src_delta_x.y);
|
||||
src_delta_y.x = src_delta_y.x - src_origin.x;
|
||||
src_delta_y.y = src_delta_y.y - src_origin.y;
|
||||
round_if_very_small(src_delta_y.x);
|
||||
round_if_very_small(src_delta_y.y);
|
||||
|
||||
DPoint const rot_origin_in_src_grid = get_mapped_point(rotated, Point(0, 0), -rotation);
|
||||
DPoint const rot_origin_in_src = convert_img_coord_precision(src, rot_origin_in_src_grid);
|
||||
|
||||
for (unsigned int y = 0; y < rotated.nb_row_tile; ++y)
|
||||
{
|
||||
for (unsigned int x = 0; x < rotated.nb_col_tile; ++x)
|
||||
{
|
||||
unsigned int const rot_tile_index = y * rotated.nb_col_tile + x;
|
||||
|
||||
for (unsigned int j = 0; j < H; ++j)
|
||||
{
|
||||
int const y_index = y * H + j;
|
||||
|
||||
for (unsigned int i = 0; i < W; ++i)
|
||||
{
|
||||
// cout << "rotated: tile[" << x << ", " << y << "] point(" << i << ", " << j << ")" << endl;
|
||||
unsigned int const rot_index = (j * W + i) * 3;
|
||||
int const x_index = x * W + i;
|
||||
Point const rot_point(x_index, y_index);
|
||||
DPoint src_rotated_point(rot_origin_in_src.x + x_index * src_delta_x.x + y_index * src_delta_y.x,
|
||||
rot_origin_in_src.y + x_index * src_delta_x.y + y_index * src_delta_y.y);
|
||||
|
||||
// cout << "rotated tile index: " << rot_tile_index << endl;
|
||||
// cout << "src point: " << src_rotated_point << endl;
|
||||
|
||||
if (src_rotated_point.x < 0 || src_rotated_point.x > src.width
|
||||
|| src_rotated_point.y < 0 || src_rotated_point.y > src.height)
|
||||
continue;
|
||||
|
||||
rotate_pixel(src, rotated,
|
||||
src_rotated_point,
|
||||
rot_tile_index, rot_index);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rotated;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
@ -851,38 +1030,58 @@ int main(int argc, char* argv[])
|
|||
|
||||
if (!check_90(argv[1]))
|
||||
{
|
||||
cerr << __LINE__ << " | 90 degrees check failed" << endl;
|
||||
cerr << __LINE__ << " | 90 degrees check failed" << endl << endl;
|
||||
// return 1;
|
||||
}
|
||||
}
|
||||
|
||||
TiledImage<8, 8> tiled_img(argv[1]);
|
||||
tiled_img.save("tiled.ppm");
|
||||
return 0;
|
||||
|
||||
Image img(argv[1]);
|
||||
TiledImage<16, 16> tiled_img(argv[1]);
|
||||
|
||||
for (double rotation = 0; rotation < 360; rotation += 5)
|
||||
for (double rotation = 0; rotation < 360; rotation += 450)
|
||||
{
|
||||
// double rotation = 45;
|
||||
// No tile
|
||||
auto const before = chrono::high_resolution_clock::now();
|
||||
|
||||
Image rotated = rotate(img, rotation);
|
||||
|
||||
Image const rotated = rotate(img, rotation);
|
||||
auto const after = chrono::high_resolution_clock::now();
|
||||
auto const duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
|
||||
|
||||
// Tile
|
||||
auto const before_tiled = chrono::high_resolution_clock::now();
|
||||
auto const rotated_tiled = rotate(tiled_img, rotation);
|
||||
auto const after_tiled = chrono::high_resolution_clock::now();
|
||||
auto const duration_ms_tiled = std::chrono::duration_cast<std::chrono::milliseconds>(after_tiled - before_tiled);
|
||||
|
||||
cout << "rotate(" << rotation << "): " << duration_ms.count() << " ms" << endl;
|
||||
// TODO: compare implementation
|
||||
cout << "tiled: " << duration_ms_tiled.count() << " ms" << endl;
|
||||
cout << "speedup: " << (int) (((float) duration_ms.count() / duration_ms_tiled.count() - 1) * 100) << "%" << endl << endl;
|
||||
|
||||
// tile check
|
||||
//tiled_img.print_tile(0);
|
||||
//rotated_tiled.print_tile(0);
|
||||
|
||||
stringstream filename;
|
||||
// filename << "/tmp/";
|
||||
// filename << "/tmp/";
|
||||
filename << "rotated_";
|
||||
|
||||
stringstream filename_tiled;
|
||||
// filename_tiled << "/tmp/";
|
||||
filename_tiled << "tiled_rotated_";
|
||||
|
||||
if (rotation < 100)
|
||||
{
|
||||
filename << "0";
|
||||
filename_tiled << "0";
|
||||
}
|
||||
if (rotation < 10)
|
||||
{
|
||||
filename << "0";
|
||||
filename_tiled << "0";
|
||||
}
|
||||
filename << rotation << ".ppm";
|
||||
filename_tiled << rotation << ".ppm";
|
||||
rotated.save(filename.str());
|
||||
rotated_tiled.save(filename_tiled.str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue