Add support for PGM format.
The code has been split into different source files. This breaks for now rotation for tiled images and PPM format. The focus is now on the PGM format.
This commit is contained in:
parent
53974a4116
commit
7e152c29fc
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
|||
.DS_Store
|
||||
*.dSYM
|
||||
*.swp
|
||||
*.o
|
||||
|
||||
rotated*.ppm
|
||||
rotated*.pnm
|
||||
*.png
|
||||
|
|
22
Makefile
22
Makefile
|
@ -1,14 +1,24 @@
|
|||
CXX = clang++
|
||||
CXXFLAGS = -std=c++11 -W -Wall -O3 -ffast-math -g -Werror
|
||||
DEFINES = -DSIMD
|
||||
LFLAGS = -flto
|
||||
DEFINES = #-DSIMD
|
||||
BUILD_DIR = /tmp
|
||||
IMG = img/lena.ppm
|
||||
SRC = rotation.cpp \
|
||||
image.cpp \
|
||||
pnm.cpp
|
||||
HEADERS = image.h \
|
||||
pnm.h
|
||||
OBJS = $(patsubst %.cpp,%.o,$(SRC))
|
||||
IMG = img/lena_3000.pgm
|
||||
|
||||
all: rotation.cpp
|
||||
$(CXX) $(CXXFLAGS) $(DEFINES) $< -o $(BUILD_DIR)/rotation
|
||||
all: $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) $(DEFINES) $(LFLAGS) $(OBJS) -o $(BUILD_DIR)/rotation
|
||||
|
||||
%.o: %.cpp $(HEADERS)
|
||||
$(CXX) $(CXXFLAGS) $(DEFINES) $< -c -o $@
|
||||
|
||||
clean:
|
||||
@rm -f *~ *.o .*.swp *.ppm cachegrind.out.*
|
||||
@rm -f *~ *.o .*.swp *.ppm *.pgm *.pnm cachegrind.out.*
|
||||
|
||||
run: all
|
||||
$(BUILD_DIR)/rotation $(IMG)
|
||||
|
@ -17,4 +27,4 @@ debug: all
|
|||
lldb $(BUILD_DIR)/rotation $(IMG)
|
||||
|
||||
cachegrind: all
|
||||
valgrind --tool=cachegrind $(BUILD_DIR)/rotation $(IMG)
|
||||
valgrind --tool=cachegrind --dsymutil=yes $(BUILD_DIR)/rotation $(IMG)
|
||||
|
|
154
image.cpp
Normal file
154
image.cpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
#include "image.h"
|
||||
#include "pnm.h"
|
||||
|
||||
Image::Image()
|
||||
: width(0)
|
||||
, height(0)
|
||||
, buffer(NULL)
|
||||
, pixel_size(0)
|
||||
{}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
delete [] buffer;
|
||||
}
|
||||
|
||||
Image::Image(unsigned int w, unsigned int h, pnm::Format type)
|
||||
{
|
||||
this->width = w;
|
||||
this->height = h;
|
||||
this->type = type;
|
||||
|
||||
switch (this->type)
|
||||
{
|
||||
case pnm::Format::PGM:
|
||||
{
|
||||
this->pixel_size = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case pnm::Format::PPM:
|
||||
{
|
||||
this->pixel_size = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = new pvalue_t[width * height * pixel_size];
|
||||
memset(buffer, 0, width * height * pixel_size * sizeof (pvalue_t));
|
||||
}
|
||||
|
||||
Image::Image(string const& path)
|
||||
: Image()
|
||||
{
|
||||
ifstream is(path);
|
||||
if (!is.is_open())
|
||||
{
|
||||
cerr << "Cannot open file '" << path << "'" << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!pnm::read_header(is, this->type, this->width, this->height))
|
||||
{
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
switch (this->type)
|
||||
{
|
||||
case pnm::Format::PGM:
|
||||
{
|
||||
this->pixel_size = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case pnm::Format::PPM:
|
||||
{
|
||||
this->pixel_size = 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!this->read_body(is))
|
||||
{
|
||||
delete buffer;
|
||||
buffer = nullptr;
|
||||
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool Image::save(string const& path) const
|
||||
{
|
||||
ofstream os(path);
|
||||
if (!os.is_open())
|
||||
{
|
||||
cerr << "Cannot open file '" << path << "'" << endl;
|
||||
return false;
|
||||
}
|
||||
pnm::write_header(os, this->type, this->width, this->height);
|
||||
this->write_body(os);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::read_body(std::ifstream& istr)
|
||||
{
|
||||
unsigned int const nb_pixels = width * height;
|
||||
buffer = new pvalue_t[nb_pixels * pixel_size];
|
||||
|
||||
pvalue_t* pixel = buffer;
|
||||
for (unsigned int i = 0; i < nb_pixels; ++i)
|
||||
{
|
||||
switch (this->type)
|
||||
{
|
||||
case pnm::Format::PGM:
|
||||
{
|
||||
pixel[0] = istr.get();
|
||||
}
|
||||
break;
|
||||
|
||||
case pnm::Format::PPM:
|
||||
{
|
||||
pixel[0] = istr.get();
|
||||
pixel[1] = istr.get();
|
||||
pixel[2] = istr.get();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pixel += pixel_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Image::write_body(std::ofstream& ostr) const
|
||||
{
|
||||
unsigned int const nb_pixels = width * height;
|
||||
pvalue_t* pixel = buffer;
|
||||
for (unsigned int i = 0; i < nb_pixels; ++i)
|
||||
{
|
||||
switch (this->type)
|
||||
{
|
||||
case pnm::Format::PGM:
|
||||
{
|
||||
ostr << (char) pixel[0];
|
||||
}
|
||||
break;
|
||||
|
||||
case pnm::Format::PPM:
|
||||
{
|
||||
ostr << (char) pixel[0];
|
||||
ostr << (char) pixel[1];
|
||||
ostr << (char) pixel[2];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pixel += pixel_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
289
image.h
Normal file
289
image.h
Normal file
|
@ -0,0 +1,289 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "pnm.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// Point
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
struct TPoint {
|
||||
T x;
|
||||
T y;
|
||||
|
||||
TPoint(T a, T b)
|
||||
: x(a)
|
||||
, y(b)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef TPoint<int> Point;
|
||||
typedef TPoint<double> DPoint; // absolute point, can be negative
|
||||
template<typename Elem, typename Traits, typename T>
|
||||
|
||||
std::basic_ostream<Elem, Traits>& operator << (std::basic_ostream<Elem, Traits>& o, TPoint<T> const& p)
|
||||
{
|
||||
o << "(" << p.x << ", " << p.y << ")";
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// Pixel
|
||||
//
|
||||
|
||||
typedef uint16_t pvalue_t; // pixel value type
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// Image
|
||||
//
|
||||
|
||||
struct Image {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
pvalue_t* buffer;
|
||||
pnm::Format type;
|
||||
size_t pixel_size;
|
||||
|
||||
Image();
|
||||
Image(unsigned int w, unsigned int h, pnm::Format type);
|
||||
Image(string const& path);
|
||||
virtual ~Image();
|
||||
|
||||
bool save(string const& path) const;
|
||||
|
||||
protected:
|
||||
virtual bool read_body(std::ifstream& istr);
|
||||
virtual bool write_body(std::ofstream& ostr) const;
|
||||
};
|
||||
|
||||
template<unsigned int W, unsigned int H>
|
||||
struct TiledImage : public Image {
|
||||
pvalue_t* tiles;
|
||||
unsigned int static const tile_w = W;
|
||||
unsigned int static const tile_h = H;
|
||||
|
||||
// two lines overlap, bottom + right
|
||||
unsigned int static const tile_size = (W + 1) * (H + 1);
|
||||
unsigned int nb_col_tile;
|
||||
unsigned int nb_row_tile;
|
||||
|
||||
TiledImage()
|
||||
: Image()
|
||||
, tiles(NULL)
|
||||
, nb_col_tile(0)
|
||||
, nb_row_tile(0)
|
||||
{}
|
||||
|
||||
~TiledImage()
|
||||
{
|
||||
delete [] tiles;
|
||||
}
|
||||
|
||||
TiledImage(unsigned int w, unsigned int h)
|
||||
{
|
||||
allocate_memory(w, h);
|
||||
}
|
||||
|
||||
TiledImage(string const& path)
|
||||
: TiledImage()
|
||||
{
|
||||
ifstream is(path);
|
||||
if (!is.is_open())
|
||||
{
|
||||
cerr << "Cannot open file '" << path << "'" << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!pnm::read_header(is, this->type, this->width, this->height))
|
||||
{
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!this->read_body(is))
|
||||
{
|
||||
// TODO: delete tiles
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
pvalue_t const*
|
||||
get_tile(unsigned int index) const
|
||||
{
|
||||
if (index >= nb_col_tile * nb_row_tile)
|
||||
return nullptr;
|
||||
|
||||
return tiles + index * tile_size * pixel_size;
|
||||
}
|
||||
|
||||
pvalue_t*
|
||||
get_tile(unsigned int index)
|
||||
{
|
||||
if (index >= nb_col_tile * nb_row_tile)
|
||||
return nullptr;
|
||||
|
||||
return tiles + index * tile_size * pixel_size;
|
||||
}
|
||||
|
||||
pvalue_t*
|
||||
access_pixel(unsigned int x, unsigned int y)
|
||||
{
|
||||
if (x >= width || y >= height)
|
||||
return nullptr;
|
||||
|
||||
unsigned int const tile_width = (tile_w + 1) * pixel_size;
|
||||
|
||||
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
|
||||
pvalue_t* tile = this->get_tile(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 * pixel_size);
|
||||
}
|
||||
|
||||
pvalue_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 + 1) * pixel_size;
|
||||
|
||||
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
|
||||
const pvalue_t* tile = this->get_tile(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 * pixel_size);
|
||||
}
|
||||
|
||||
void
|
||||
print_tile(unsigned int index) const
|
||||
{
|
||||
cout << "Tile[" << index << "]" << endl;
|
||||
pvalue_t const* tile = this->get_tile(index);
|
||||
unsigned int const tile_width = (tile_w + 1) * pixel_size;
|
||||
for (unsigned int j = 0; j < tile_h + 1; ++j)
|
||||
{
|
||||
for (unsigned int i = 0; i < tile_w + 1; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
cout << ", ";
|
||||
pvalue_t const* p = tile + j * tile_width + i * pixel_size;
|
||||
cout << (int) *p << " " << (int) *(p + 1) << " " << (int) *(p + 2);
|
||||
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
void fill_overlap()
|
||||
{
|
||||
unsigned int const tile_width = (W + 1) * pixel_size;
|
||||
|
||||
for (int j = nb_row_tile - 1; j >= 0; --j)
|
||||
for (unsigned int i = 0; i < nb_col_tile; ++i)
|
||||
{
|
||||
// copy last line overlap
|
||||
if (j != (int) nb_row_tile - 1)
|
||||
{
|
||||
pvalue_t const* tile_src = this->access_pixel(i * W, (j + 1) * H);
|
||||
pvalue_t* tile_dst = this->access_pixel(i * W, j * H);
|
||||
tile_dst += H * tile_width;
|
||||
memcpy(tile_dst, tile_src, tile_width * sizeof (pvalue_t));
|
||||
}
|
||||
|
||||
// copy last col overlap
|
||||
if (i != nb_col_tile - 1)
|
||||
{
|
||||
pvalue_t* tile_src = this->get_tile(i + 1 + j * nb_col_tile);
|
||||
pvalue_t* tile_dst = this->get_tile(i + j * nb_col_tile);
|
||||
tile_dst += W * pixel_size;
|
||||
for (unsigned int y = 0; y < H; ++y)
|
||||
{
|
||||
memcpy(tile_dst, tile_src, pixel_size * sizeof (pvalue_t));
|
||||
tile_src += tile_width;
|
||||
tile_dst += tile_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
void allocate_memory(unsigned int w, unsigned int h)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
|
||||
nb_col_tile = width / tile_w;
|
||||
if (width % tile_w != 0)
|
||||
++nb_col_tile;
|
||||
|
||||
nb_row_tile = height / tile_h;
|
||||
if (height % tile_h != 0)
|
||||
++nb_row_tile;
|
||||
|
||||
unsigned int const nb_tiles = nb_col_tile * nb_row_tile;
|
||||
tiles = new pvalue_t[nb_tiles * tile_size * pixel_size];
|
||||
memset(tiles, 0, nb_tiles * tile_size * pixel_size * sizeof (pvalue_t));
|
||||
}
|
||||
|
||||
virtual bool read_body(std::ifstream& istr) override
|
||||
{
|
||||
this->allocate_memory(width, height);
|
||||
|
||||
// Pixel loading
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
{
|
||||
pvalue_t* tile = this->access_pixel(i, j);
|
||||
tile[0] = istr.get();
|
||||
tile[1] = istr.get();
|
||||
tile[2] = istr.get();
|
||||
|
||||
tile += pixel_size;
|
||||
}
|
||||
|
||||
this->fill_overlap();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool write_body(std::ofstream& ostr) const override
|
||||
{
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
{
|
||||
pvalue_t const* tile = this->access_pixel(i, j);
|
||||
ostr << (char) tile[0];
|
||||
ostr << (char) tile[1];
|
||||
ostr << (char) tile[2];
|
||||
|
||||
tile += pixel_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
86
pnm.cpp
Normal file
86
pnm.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
#include <fstream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "pnm.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace pnm
|
||||
{
|
||||
|
||||
bool read_header(std::ifstream& istr, Format& type, unsigned int& width, unsigned int& height)
|
||||
{
|
||||
// check magic
|
||||
if (istr.get() != 'P' )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char c_type = static_cast<char>(istr.get());
|
||||
if (c_type == '6')
|
||||
{
|
||||
type = Format::PPM;
|
||||
}
|
||||
else if (c_type == '5')
|
||||
{
|
||||
type = Format::PGM;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Invalid PNM format." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (istr.get() != '\n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip comments
|
||||
while (istr.peek() == '#')
|
||||
{
|
||||
std::string line;
|
||||
std::getline(istr, line);
|
||||
}
|
||||
|
||||
// get size
|
||||
istr >> width >> height;
|
||||
if (width == 0 || height == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// get maxvalue
|
||||
if (istr.get() != '\n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int max_value = -1;
|
||||
istr >> max_value;
|
||||
if (max_value > 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (istr.get() != '\n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_header(std::ofstream& ostr, Format type, unsigned int width, unsigned int height)
|
||||
{
|
||||
if (type == Format::PGM)
|
||||
ostr << "P5" << endl;
|
||||
if (type == Format::PPM)
|
||||
ostr << "P6" << endl;
|
||||
ostr << width << " " << height << endl;
|
||||
ostr << "255" << endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end of namespace pnm
|
16
pnm.h
Normal file
16
pnm.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace pnm
|
||||
{
|
||||
|
||||
enum class Format {
|
||||
PGM,
|
||||
PPM
|
||||
};
|
||||
|
||||
bool read_header(std::ifstream& istr, Format& type, unsigned int& width, unsigned int& height);
|
||||
bool write_header(std::ofstream& ostr, Format type, unsigned int width, unsigned int height);
|
||||
|
||||
}
|
512
rotation.cpp
512
rotation.cpp
|
@ -12,428 +12,10 @@
|
|||
#include <xmmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
#include "image.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
//
|
||||
//
|
||||
// Point
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
struct TPoint {
|
||||
T x;
|
||||
T y;
|
||||
|
||||
TPoint(T a, T b)
|
||||
: x(a)
|
||||
, y(b)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef TPoint<int> Point;
|
||||
typedef TPoint<double> DPoint; // absolute point, can be negative
|
||||
template<typename Elem, typename Traits, typename T>
|
||||
|
||||
std::basic_ostream<Elem, Traits>& operator << (std::basic_ostream<Elem, Traits>& o, TPoint<T> const& p)
|
||||
{
|
||||
o << "(" << p.x << ", " << p.y << ")";
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// Pixel
|
||||
//
|
||||
|
||||
typedef uint16_t pvalue_t; // pixel value type
|
||||
#define PIXEL_SIZE 4
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// Image
|
||||
//
|
||||
|
||||
struct Image {
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
pvalue_t* buffer;
|
||||
|
||||
Image()
|
||||
: width(0)
|
||||
, height(0)
|
||||
, buffer(NULL)
|
||||
{}
|
||||
|
||||
virtual ~Image()
|
||||
{
|
||||
delete [] buffer;
|
||||
}
|
||||
|
||||
Image(unsigned int w, unsigned int h)
|
||||
{
|
||||
this->width = w;
|
||||
this->height = h;
|
||||
buffer = new pvalue_t[width * height * PIXEL_SIZE];
|
||||
memset(buffer, 0, width * height * PIXEL_SIZE * sizeof (pvalue_t));
|
||||
}
|
||||
|
||||
Image(string const& path)
|
||||
: Image()
|
||||
{
|
||||
ifstream is(path);
|
||||
if (!is.is_open())
|
||||
{
|
||||
cerr << "Cannot open file '" << path << "'" << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!this->read_header(is))
|
||||
{
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!this->read_body(is))
|
||||
{
|
||||
delete buffer;
|
||||
buffer = nullptr;
|
||||
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
bool save(string const& path) const
|
||||
{
|
||||
ofstream os(path);
|
||||
if (!os.is_open())
|
||||
{
|
||||
cerr << "Cannot open file '" << path << "'" << endl;
|
||||
return false;
|
||||
}
|
||||
this->write_header(os);
|
||||
this->write_body(os);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
bool read_header(std::ifstream& istr)
|
||||
{
|
||||
// check magic
|
||||
if (istr.get() != 'P' )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char type = static_cast<char>(istr.get());
|
||||
if (type != '6')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (istr.get() != '\n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip comments
|
||||
while (istr.peek() == '#')
|
||||
{
|
||||
std::string line;
|
||||
std::getline(istr, line);
|
||||
}
|
||||
|
||||
// get size
|
||||
istr >> width >> height;
|
||||
if (width == 0 || height == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// get maxvalue
|
||||
if (istr.get() != '\n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int max_value = -1;
|
||||
istr >> max_value;
|
||||
if (max_value > 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (istr.get() != '\n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// cout << "width: " << width << endl;
|
||||
// cout << "height: " << height << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write_header(std::ofstream& ostr) const
|
||||
{
|
||||
ostr << "P6" << endl;
|
||||
ostr << width << " " << height << endl;
|
||||
ostr << "255" << endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool read_body(std::ifstream& istr)
|
||||
{
|
||||
unsigned int const nb_pixels = width * height;
|
||||
buffer = new pvalue_t[nb_pixels * PIXEL_SIZE];
|
||||
|
||||
pvalue_t* pixel = buffer;
|
||||
for (unsigned int i = 0; i < nb_pixels; ++i)
|
||||
{
|
||||
pixel[0] = istr.get();
|
||||
pixel[1] = istr.get();
|
||||
pixel[2] = istr.get();
|
||||
|
||||
pixel += PIXEL_SIZE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool write_body(std::ofstream& ostr) const
|
||||
{
|
||||
unsigned int const nb_pixels = width * height;
|
||||
pvalue_t* pixel = buffer;
|
||||
for (unsigned int i = 0; i < nb_pixels; ++i)
|
||||
{
|
||||
ostr << (char) pixel[0];
|
||||
ostr << (char) pixel[1];
|
||||
ostr << (char) pixel[2];
|
||||
|
||||
pixel += PIXEL_SIZE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<unsigned int W, unsigned int H>
|
||||
struct TiledImage : public Image {
|
||||
pvalue_t* tiles;
|
||||
unsigned int static const tile_w = W;
|
||||
unsigned int static const tile_h = H;
|
||||
|
||||
// two lines overlap, bottom + right
|
||||
unsigned int static const tile_size = (W + 1) * (H + 1);
|
||||
unsigned int nb_col_tile;
|
||||
unsigned int nb_row_tile;
|
||||
|
||||
TiledImage()
|
||||
: Image()
|
||||
, tiles(NULL)
|
||||
, nb_col_tile(0)
|
||||
, nb_row_tile(0)
|
||||
{}
|
||||
|
||||
~TiledImage()
|
||||
{
|
||||
delete [] tiles;
|
||||
}
|
||||
|
||||
TiledImage(unsigned int w, unsigned int h)
|
||||
{
|
||||
allocate_memory(w, h);
|
||||
}
|
||||
|
||||
TiledImage(string const& path)
|
||||
: TiledImage()
|
||||
{
|
||||
ifstream is(path);
|
||||
if (!is.is_open())
|
||||
{
|
||||
cerr << "Cannot open file '" << path << "'" << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!this->read_header(is))
|
||||
{
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!this->read_body(is))
|
||||
{
|
||||
// TODO: delete tiles
|
||||
cerr << "Invalid header." << endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
pvalue_t const*
|
||||
get_tile(unsigned int index) const
|
||||
{
|
||||
if (index >= nb_col_tile * nb_row_tile)
|
||||
return nullptr;
|
||||
|
||||
return tiles + index * tile_size * PIXEL_SIZE;
|
||||
}
|
||||
|
||||
pvalue_t*
|
||||
get_tile(unsigned int index)
|
||||
{
|
||||
if (index >= nb_col_tile * nb_row_tile)
|
||||
return nullptr;
|
||||
|
||||
return tiles + index * tile_size * PIXEL_SIZE;
|
||||
}
|
||||
|
||||
pvalue_t*
|
||||
access_pixel(unsigned int x, unsigned int y)
|
||||
{
|
||||
if (x >= width || y >= height)
|
||||
return nullptr;
|
||||
|
||||
unsigned int const tile_width = (tile_w + 1) * PIXEL_SIZE;
|
||||
|
||||
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
|
||||
pvalue_t* tile = this->get_tile(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 * PIXEL_SIZE);
|
||||
}
|
||||
|
||||
pvalue_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 + 1) * PIXEL_SIZE;
|
||||
|
||||
unsigned int const tile_index = (y / tile_h) * nb_col_tile + (x / tile_w);
|
||||
const pvalue_t* tile = this->get_tile(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 * PIXEL_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
print_tile(unsigned int index) const
|
||||
{
|
||||
cout << "Tile[" << index << "]" << endl;
|
||||
pvalue_t const* tile = this->get_tile(index);
|
||||
unsigned int const tile_width = (tile_w + 1) * PIXEL_SIZE;
|
||||
for (unsigned int j = 0; j < tile_h + 1; ++j)
|
||||
{
|
||||
for (unsigned int i = 0; i < tile_w + 1; ++i)
|
||||
{
|
||||
if (i != 0)
|
||||
cout << ", ";
|
||||
pvalue_t const* p = tile + j * tile_width + i * PIXEL_SIZE;
|
||||
cout << (int) *p << " " << (int) *(p + 1) << " " << (int) *(p + 2);
|
||||
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
void fill_overlap()
|
||||
{
|
||||
unsigned int const tile_width = (W + 1) * PIXEL_SIZE;
|
||||
|
||||
for (int j = nb_row_tile - 1; j >= 0; --j)
|
||||
for (unsigned int i = 0; i < nb_col_tile; ++i)
|
||||
{
|
||||
// copy last line overlap
|
||||
if (j != (int) nb_row_tile - 1)
|
||||
{
|
||||
pvalue_t const* tile_src = this->access_pixel(i * W, (j + 1) * H);
|
||||
pvalue_t* tile_dst = this->access_pixel(i * W, j * H);
|
||||
tile_dst += H * tile_width;
|
||||
memcpy(tile_dst, tile_src, tile_width * sizeof (pvalue_t));
|
||||
}
|
||||
|
||||
// copy last col overlap
|
||||
if (i != nb_col_tile - 1)
|
||||
{
|
||||
pvalue_t* tile_src = this->get_tile(i + 1 + j * nb_col_tile);
|
||||
pvalue_t* tile_dst = this->get_tile(i + j * nb_col_tile);
|
||||
tile_dst += W * PIXEL_SIZE;
|
||||
for (unsigned int y = 0; y < H; ++y)
|
||||
{
|
||||
memcpy(tile_dst, tile_src, PIXEL_SIZE * sizeof (pvalue_t));
|
||||
tile_src += tile_width;
|
||||
tile_dst += tile_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
void allocate_memory(unsigned int w, unsigned int h)
|
||||
{
|
||||
width = w;
|
||||
height = h;
|
||||
|
||||
nb_col_tile = width / tile_w;
|
||||
if (width % tile_w != 0)
|
||||
++nb_col_tile;
|
||||
|
||||
nb_row_tile = height / tile_h;
|
||||
if (height % tile_h != 0)
|
||||
++nb_row_tile;
|
||||
|
||||
unsigned int const nb_tiles = nb_col_tile * nb_row_tile;
|
||||
tiles = new pvalue_t[nb_tiles * tile_size * PIXEL_SIZE];
|
||||
memset(tiles, 0, nb_tiles * tile_size * PIXEL_SIZE * sizeof (pvalue_t));
|
||||
}
|
||||
|
||||
virtual bool read_body(std::ifstream& istr) override
|
||||
{
|
||||
this->allocate_memory(width, height);
|
||||
|
||||
// Pixel loading
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
{
|
||||
pvalue_t* tile = this->access_pixel(i, j);
|
||||
tile[0] = istr.get();
|
||||
tile[1] = istr.get();
|
||||
tile[2] = istr.get();
|
||||
|
||||
tile += PIXEL_SIZE;
|
||||
}
|
||||
|
||||
this->fill_overlap();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool write_body(std::ofstream& ostr) const override
|
||||
{
|
||||
for (unsigned int j = 0; j < height; ++j)
|
||||
for (unsigned int i = 0; i < width; ++i)
|
||||
{
|
||||
pvalue_t const* tile = this->access_pixel(i, j);
|
||||
ostr << (char) tile[0];
|
||||
ostr << (char) tile[1];
|
||||
ostr << (char) tile[2];
|
||||
|
||||
tile += PIXEL_SIZE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
|
@ -589,12 +171,12 @@ void rotate_pixel(Image const& src,
|
|||
int const src_x = src_rotated_point.x >> 3;
|
||||
int const src_y = src_rotated_point.y >> 3;
|
||||
|
||||
unsigned int src_index = (src_y * src.width + src_x) * PIXEL_SIZE;
|
||||
unsigned int src_index = (src_y * src.width + src_x) * src.pixel_size;
|
||||
|
||||
// Bilinear interpolation
|
||||
unsigned int src_index_1 = src_index;
|
||||
unsigned int src_index_3 = src_index_1 + PIXEL_SIZE * src.width;
|
||||
unsigned int src_index_4 = src_index_3 + PIXEL_SIZE;
|
||||
unsigned int src_index_3 = src_index_1 + src.pixel_size * src.width;
|
||||
unsigned int src_index_4 = src_index_3 + src.pixel_size;
|
||||
|
||||
// Out-of-bounds check
|
||||
if (src_index_4 >= src_limit)
|
||||
|
@ -607,14 +189,14 @@ void rotate_pixel(Image const& src,
|
|||
|
||||
#ifndef SIMD
|
||||
|
||||
unsigned int src_index_2 = src_index_1 + PIXEL_SIZE;
|
||||
unsigned int src_index_2 = src_index_1 + src.pixel_size;
|
||||
|
||||
rotate_buffer[rot_index] = ((src.buffer[src_index_1] * inv_x + src.buffer[src_index_2] * x_delta) * inv_y
|
||||
+ (src.buffer[src_index_3] * inv_x + src.buffer[src_index_4] * x_delta) * y_delta) >> 6;
|
||||
rotate_buffer[rot_index + 1] = ((src.buffer[src_index_1 + 1] * inv_x + src.buffer[src_index_2 + 1] * x_delta) * inv_y
|
||||
+ (src.buffer[src_index_3 + 1] * inv_x + src.buffer[src_index_4 + 1] * x_delta) * y_delta) >> 6;
|
||||
rotate_buffer[rot_index + 2] = ((src.buffer[src_index_1 + 2] * inv_x + src.buffer[src_index_2 + 2] * x_delta) * inv_y
|
||||
+ (src.buffer[src_index_3 + 2] * inv_x + src.buffer[src_index_4 + 2] * x_delta) * y_delta) >> 6;
|
||||
// rotate_buffer[rot_index + 1] = ((src.buffer[src_index_1 + 1] * inv_x + src.buffer[src_index_2 + 1] * x_delta) * inv_y
|
||||
// + (src.buffer[src_index_3 + 1] * inv_x + src.buffer[src_index_4 + 1] * x_delta) * y_delta) >> 6;
|
||||
// rotate_buffer[rot_index + 2] = ((src.buffer[src_index_1 + 2] * inv_x + src.buffer[src_index_2 + 2] * x_delta) * inv_y
|
||||
// + (src.buffer[src_index_3 + 2] * inv_x + src.buffer[src_index_4 + 2] * x_delta) * y_delta) >> 6;
|
||||
|
||||
#else
|
||||
|
||||
|
@ -635,8 +217,8 @@ void rotate_pixel(Image const& src,
|
|||
top = _mm_srli_epi16(top, 6);
|
||||
|
||||
rotate_buffer[rot_index] = _mm_extract_epi16(top, 0) + _mm_extract_epi16(top, 4);
|
||||
rotate_buffer[rot_index + 1] = _mm_extract_epi16(top, 1) + _mm_extract_epi16(top, 5);
|
||||
rotate_buffer[rot_index + 2] = _mm_extract_epi16(top, 2) + _mm_extract_epi16(top, 6);
|
||||
// rotate_buffer[rot_index + 1] = _mm_extract_epi16(top, 1) + _mm_extract_epi16(top, 5);
|
||||
// rotate_buffer[rot_index + 2] = _mm_extract_epi16(top, 2) + _mm_extract_epi16(top, 6);
|
||||
|
||||
|
||||
#endif // ! SIMD
|
||||
|
@ -648,7 +230,7 @@ Image* rotate(Image const& src, double angle)
|
|||
unsigned int w = 0;
|
||||
unsigned int h = 0;
|
||||
compute_output_size(src, rotation, w, h);
|
||||
Image* rotated = new Image(w, h);
|
||||
Image* rotated = new Image(w, h, src.type);
|
||||
|
||||
// corner points in rotated image
|
||||
// TODO: add one ligne for smooth border
|
||||
|
@ -672,7 +254,7 @@ Image* rotate(Image const& src, double angle)
|
|||
round_if_very_small(src_delta_y.x);
|
||||
round_if_very_small(src_delta_y.y);
|
||||
|
||||
unsigned int const src_limit = src.width * src.height * PIXEL_SIZE;
|
||||
unsigned int const src_limit = src.width * src.height * src.pixel_size;
|
||||
|
||||
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);
|
||||
|
@ -702,7 +284,7 @@ Image* rotate(Image const& src, double angle)
|
|||
buffer, buffer_index);
|
||||
}
|
||||
|
||||
buffer_index += PIXEL_SIZE;
|
||||
buffer_index += rotated->pixel_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -725,7 +307,7 @@ void rotate_pixel(TiledImage<W, H> const& src,
|
|||
int const src_y = src_rotated_point.y >> 3;
|
||||
|
||||
pvalue_t const* src_index_1 = src.access_pixel(src_x, src_y);
|
||||
pvalue_t const* src_index_3 = src_index_1 + (W + 1) * PIXEL_SIZE;
|
||||
pvalue_t const* src_index_3 = src_index_1 + (W + 1) * src.pixel_size;
|
||||
|
||||
unsigned int x_delta = src_rotated_point.x & 0x07;;
|
||||
unsigned int y_delta = src_rotated_point.y & 0x07;
|
||||
|
@ -734,8 +316,8 @@ void rotate_pixel(TiledImage<W, H> const& src,
|
|||
|
||||
#ifndef SIMD
|
||||
|
||||
pvalue_t const* src_index_2 = src_index_1 + PIXEL_SIZE;
|
||||
pvalue_t const* src_index_4 = src_index_3 + PIXEL_SIZE;
|
||||
pvalue_t const* src_index_2 = src_index_1 + src.pixel_size;
|
||||
pvalue_t const* src_index_4 = src_index_3 + src.pixel_size;
|
||||
|
||||
rot_tile[0] = ((src_index_1[0] * inv_x + src_index_2[0] * x_delta) * inv_y
|
||||
+ (src_index_3[0] * inv_x + src_index_4[0] * x_delta) * y_delta) >> 6;
|
||||
|
@ -824,11 +406,11 @@ rotate(TiledImage<W, H> const& src, double angle)
|
|||
rotate_pixel(src, src_runner, runner);
|
||||
}
|
||||
|
||||
runner += PIXEL_SIZE;
|
||||
runner += rotated->pixel_size;
|
||||
}
|
||||
|
||||
// Jump overlapping pixel
|
||||
runner += PIXEL_SIZE;
|
||||
runner += rotated->pixel_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -847,7 +429,7 @@ rotate(TiledImage<W, H> const& src, double angle)
|
|||
|
||||
bool check_points()
|
||||
{
|
||||
Image five(5, 5);
|
||||
Image five(5, 5, pnm::Format::PGM);
|
||||
Point origin(0, 0);
|
||||
DPoint d1 = convert_grid_coord(five, origin);
|
||||
assert(d1.x == -2);
|
||||
|
@ -858,7 +440,7 @@ bool check_points()
|
|||
|
||||
bool check_trigo()
|
||||
{
|
||||
Image square(500, 500);
|
||||
Image square(500, 500, pnm::Format::PGM);
|
||||
double const ratio = compute_ratio(square);
|
||||
double const sigma = 1.0e-2;
|
||||
if (!fequal(ratio, 1 / 707.106, sigma))
|
||||
|
@ -903,7 +485,7 @@ bool check_trigo()
|
|||
}
|
||||
|
||||
|
||||
Image rotated(w, h);
|
||||
Image rotated(w, h, pnm::Format::PGM);
|
||||
|
||||
DPoint const a_p45 = convert_abs_coord(angle + rotation, ratio);
|
||||
Point const p45 = convert_img_coord(rotated, a_p45);
|
||||
|
@ -953,9 +535,9 @@ bool check_90(string const& path)
|
|||
{
|
||||
for (unsigned int x = 0; x < rotated->width; ++x)
|
||||
{
|
||||
unsigned rot_index = (y * rotated->width + x) * 3;
|
||||
unsigned src_index = (x * src.width + (src.width - 1 - y)) * 3;
|
||||
if (memcmp(&rotated->buffer[rot_index], &src.buffer[src_index], 3 * sizeof (uint8_t)) != 0)
|
||||
unsigned rot_index = (y * rotated->width + x) * rotated->pixel_size;
|
||||
unsigned src_index = (x * src.width + (src.width - 1 - y)) * src.pixel_size;
|
||||
if (memcmp(&rotated->buffer[rot_index], &src.buffer[src_index], src.pixel_size * sizeof (pvalue_t)) != 0)
|
||||
{
|
||||
Point r(x, y);
|
||||
Point s((src.width - 1 - y), x);
|
||||
|
@ -988,7 +570,7 @@ string get_save_path(string const& base, unsigned int i)
|
|||
filename << "0";
|
||||
if (i < 10)
|
||||
filename << "0";
|
||||
filename << i << ".ppm";
|
||||
filename << i << ".pnm";
|
||||
|
||||
return filename.str();
|
||||
}
|
||||
|
@ -1021,6 +603,7 @@ int main(int argc, char* argv[])
|
|||
double const step = 15;
|
||||
bool save_output_img = false;
|
||||
bool print_each_run = false;
|
||||
bool test_tile = false;
|
||||
|
||||
// No tile
|
||||
Image img(argv[1]);
|
||||
|
@ -1048,29 +631,32 @@ int main(int argc, char* argv[])
|
|||
cout << " average: " << average / i << "ms" << endl << endl;
|
||||
|
||||
// Tile
|
||||
TiledImage<32, 32> tiled_img(argv[1]);
|
||||
average = 0.0;
|
||||
i = 0;
|
||||
cout << "Tiled image" << endl;
|
||||
for (double rotation = 0; rotation < 360; rotation += step)
|
||||
if (test_tile)
|
||||
{
|
||||
auto const before = chrono::high_resolution_clock::now();
|
||||
auto const rotated = rotate(tiled_img, rotation);
|
||||
auto const after = chrono::high_resolution_clock::now();
|
||||
auto const duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
|
||||
average += duration_ms.count();
|
||||
TiledImage<32, 32> tiled_img(argv[1]);
|
||||
average = 0.0;
|
||||
i = 0;
|
||||
cout << "Tiled image" << endl;
|
||||
for (double rotation = 0; rotation < 360; rotation += step)
|
||||
{
|
||||
auto const before = chrono::high_resolution_clock::now();
|
||||
auto const rotated = rotate(tiled_img, rotation);
|
||||
auto const after = chrono::high_resolution_clock::now();
|
||||
auto const duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(after - before);
|
||||
average += duration_ms.count();
|
||||
|
||||
if (print_each_run)
|
||||
cout << "rotate tiled(" << rotation << "): " << duration_ms.count() << " ms" << endl;
|
||||
if (print_each_run)
|
||||
cout << "rotate tiled(" << rotation << "): " << duration_ms.count() << " ms" << endl;
|
||||
|
||||
if (save_output_img)
|
||||
rotated->save(get_save_path("rotated_tiled", rotation));
|
||||
if (save_output_img)
|
||||
rotated->save(get_save_path("rotated_tiled", rotation));
|
||||
|
||||
delete rotated;
|
||||
++i;
|
||||
delete rotated;
|
||||
++i;
|
||||
}
|
||||
cout << "---------" << endl;
|
||||
cout << " average: " << average / i << "ms" << endl;
|
||||
}
|
||||
cout << "---------" << endl;
|
||||
cout << " average: " << average / i << "ms" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue