Fix tiled rotation.
- Add TiledImage::access_pixel(). - Add TiledImage::print_tile(). - Compare implementations.
This commit is contained in:
parent
007ad2284e
commit
7e33e85909
253
rotation.cpp
253
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:
|
protected:
|
||||||
void allocate_memory(unsigned int w, unsigned int h)
|
void allocate_memory(unsigned int w, unsigned int h)
|
||||||
{
|
{
|
||||||
|
@ -277,19 +330,11 @@ struct TiledImage : public Image {
|
||||||
{
|
{
|
||||||
this->allocate_memory(width, height);
|
this->allocate_memory(width, height);
|
||||||
|
|
||||||
unsigned int const tile_width = tile_w * 3;
|
|
||||||
|
|
||||||
// Pixel loading
|
// Pixel loading
|
||||||
for (unsigned int j = 0; j < height; ++j)
|
for (unsigned int j = 0; j < height; ++j)
|
||||||
for (unsigned int i = 0; i < width; ++i)
|
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 = this->access_pixel(i, j);
|
||||||
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);
|
|
||||||
*(tile++) = istr.get();
|
*(tile++) = istr.get();
|
||||||
*(tile++) = istr.get();
|
*(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
|
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 j = 0; j < height; ++j)
|
||||||
for (unsigned int i = 0; i < width; ++i)
|
for (unsigned int i = 0; i < width; ++i)
|
||||||
{
|
{
|
||||||
unsigned int const tile_index = (j / tile_h) * nb_col_tile + (i / tile_w);
|
uint8_t const* tile = this->access_pixel(i, j);
|
||||||
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);
|
|
||||||
ostr << (char) *(tile++);
|
ostr << (char) *(tile++);
|
||||||
ostr << (char) *(tile++);
|
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)
|
if (abs(d) < 1.0e-10)
|
||||||
d = 0.0;
|
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);
|
double y_delta = src_rotated_point.y - floor(src_rotated_point.y);
|
||||||
round_if_very_small(y_delta);
|
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
|
// SIMD
|
||||||
__m128 const x_d = _mm_set_ps1(x_delta);
|
__m128 const x_d = _mm_set_ps1(x_delta);
|
||||||
__m128 const inv_x_d = _mm_set_ps1(1 - 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;
|
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]))
|
if (!check_90(argv[1]))
|
||||||
{
|
{
|
||||||
cerr << __LINE__ << " | 90 degrees check failed" << endl;
|
cerr << __LINE__ << " | 90 degrees check failed" << endl << endl;
|
||||||
// return 1;
|
// return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TiledImage<8, 8> tiled_img(argv[1]);
|
|
||||||
tiled_img.save("tiled.ppm");
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
Image img(argv[1]);
|
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();
|
auto const before = chrono::high_resolution_clock::now();
|
||||||
|
Image const rotated = rotate(img, rotation);
|
||||||
Image rotated = rotate(img, rotation);
|
|
||||||
|
|
||||||
auto const after = chrono::high_resolution_clock::now();
|
auto const after = chrono::high_resolution_clock::now();
|
||||||
auto const duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
|
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;
|
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;
|
stringstream filename;
|
||||||
// filename << "/tmp/";
|
// filename << "/tmp/";
|
||||||
filename << "rotated_";
|
filename << "rotated_";
|
||||||
|
|
||||||
|
stringstream filename_tiled;
|
||||||
|
// filename_tiled << "/tmp/";
|
||||||
|
filename_tiled << "tiled_rotated_";
|
||||||
|
|
||||||
if (rotation < 100)
|
if (rotation < 100)
|
||||||
|
{
|
||||||
filename << "0";
|
filename << "0";
|
||||||
|
filename_tiled << "0";
|
||||||
|
}
|
||||||
if (rotation < 10)
|
if (rotation < 10)
|
||||||
|
{
|
||||||
filename << "0";
|
filename << "0";
|
||||||
|
filename_tiled << "0";
|
||||||
|
}
|
||||||
filename << rotation << ".ppm";
|
filename << rotation << ".ppm";
|
||||||
|
filename_tiled << rotation << ".ppm";
|
||||||
rotated.save(filename.str());
|
rotated.save(filename.str());
|
||||||
|
rotated_tiled.save(filename_tiled.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in a new issue