Skip to content

Commit

Permalink
Flipping and transposing
Browse files Browse the repository at this point in the history
  • Loading branch information
erostensc committed Dec 1, 2021
1 parent eaeaa54 commit db57afe
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 50 deletions.
2 changes: 1 addition & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ printlibs:

.PHONY: test

REGRESSIONS=distance_transform_test fast_corner_test load_and_save image_ref convolution
REGRESSIONS=distance_transform_test fast_corner_test load_and_save image_ref convolution flips
REGRESSION_OUT=$(patsubst %,tests/%.out, $(REGRESSIONS))

test:$(REGRESSION_OUT)
Expand Down
13 changes: 13 additions & 0 deletions cvd/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,22 @@ namespace Internal
struct ImagePromise
{
};

[[noreturn]] void error_abort(const char* f, int l, const char* code);
};
#endif


#ifdef CVD_DEBUG
#define CVD_ASSERT(X) do{if(!(X))CVD::Internal::error_abort(__FILE__, __LINE__, #X);}while(0)
#elif defined(_MSC_VER)
#define CVD_ASSERT(X) __assume(X)
#elif defined(__GNUC__) || defined(__clang__)
#define CVD_ASSERT(X) do{if(!(X)) __builtin_unreachable();}while(0)
#elif
#define CVD_ASSERT(X) do{}while(0)
#endif

#ifdef CVD_IMAGE_DEBUG
#define CVD_IMAGE_ASSERT(X, Y) \
if(!(X)) \
Expand Down
4 changes: 4 additions & 0 deletions cvd/image_ref.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ class ImageRef
///@overload
constexpr ImageRef shiftr(int i) const;

constexpr ImageRef transpose() const{
return ImageRef(y,x);
}

// and now the data members (which are public!)
int x; ///< The x co-ordinate
int y; ///< The y co-ordinate
Expand Down
70 changes: 60 additions & 10 deletions cvd/vision.h
Original file line number Diff line number Diff line change
Expand Up @@ -556,27 +556,77 @@ Image<T> warp(const BasicImage<T>& in, const CAM1& cam_in, const CAM2& cam_out)

#endif

namespace Internal{
namespace Internal
{
template <class T>
void simpleTranspose(const SubImage<T>& in, SubImage<T> out)
{
CVD_ASSERT(in.size().transpose() == out.size());
for(int r = 0; r < in.size().y; r++)
for(int c = 0; c < in.size().x; c++)
out[c][r] = in[r][c];
}

template <class T>
Image<T> simpleTranspose(const SubImage<T>& in)
void recursiveTranspose(const SubImage<T>& in, SubImage<T> out, const int bytes = 2048)
{
Image<T> out(ImageRef(in.size().y, in.size().x));
for(int r=0; r < in.size().y; r++)
for(int c=0; c < in.size().x; c++)
out[c][r] = in[r][c];

return out;
CVD_ASSERT(in.size().transpose() == out.size());

if(in.size().area() * static_cast<int>(sizeof(T)) < bytes || in.size().x == 1 || in.size().y == 1)
simpleTranspose(in, out);
else if(in.size().x >= in.size().y)
{
//The image is very wide, so the strategy of picking largest-sqare-and-remainder
//can lead to linear recursion depth, so instead split it in half

const int width_left = in.size().x / 2;
const int width_right = in.size().x - width_left;
const ImageRef left_chunk { width_left, in.size().y };
const ImageRef right_chunk { width_right, in.size().y };
const ImageRef right_start { width_left, 0 };

recursiveTranspose(in.sub_image(ImageRef(0, 0), left_chunk), out.sub_image(ImageRef(0, 0), left_chunk.transpose()));
recursiveTranspose(in.sub_image(right_start, right_chunk), out.sub_image(right_start.transpose(), right_chunk.transpose()));
}
else
{
const int height_top = in.size().y / 2;
const int height_bottom = in.size().y - height_top;
ImageRef top_chunk { in.size().x, height_top };
ImageRef bottom_chunk { in.size().x, height_bottom };
ImageRef bottom_start { 0, height_top };

recursiveTranspose(in.sub_image(ImageRef(0, 0), top_chunk), out.sub_image(ImageRef(0, 0), top_chunk.transpose()));
recursiveTranspose(in.sub_image(bottom_start, bottom_chunk), out.sub_image(bottom_start.transpose(), bottom_chunk.transpose()));
}
}

}

template <class T>
void transpose(const SubImage<T>& in, SubImage<T>&& out)
{
CVD_ASSERT(in.size().transpose() == out.size());
Internal::recursiveTranspose(in, out);
}

template <class T>
Image<T> transpose(const SubImage<T>& in)
{
Image<T> out(in.size().transpose());
Internal::recursiveTranspose(in, out);
return out;
}

/// flips an image vertically in place.
template <class T>
void flipVertical(SubImage<T>&& in)
{
for(int r=0; r < in.size().y/2; r++)
for(int c=0; c < in.size().x; c++){
std::swap(in[r][c], in[in.size().y-1-r][c]);
for(int r = 0; r < in.size().y / 2; r++)
for(int c = 0; c < in.size().x; c++)
{
std::swap(in[r][c], in[in.size().y - 1 - r][c]);
}
}

Expand Down
6 changes: 6 additions & 0 deletions cvd_src/exceptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,9 @@ CVD::Exceptions::VideoBuffer::BadColourSpace::BadColourSpace(const std::string&
buffer + " can not grab video in the " + c + "colourspace on the specified device.")
{
}

[[noreturn]] void CVD::Internal::error_abort(const char* f, int l, const char* code){
std::cerr << "Assertion failed at " << f << ": " << l << " " << code << "\n";
std::abort();
}

4 changes: 4 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ add_test(NAME load_and_save COMMAND load_and_save)
add_executable(convolution convolution.cc)
target_link_libraries(convolution PRIVATE CVD)
add_test(NAME convolution COMMAND convolution)

add_executable(flips flips.cc)
target_link_libraries(flips PRIVATE CVD)
add_test(NAME flips COMMAND flips)
116 changes: 77 additions & 39 deletions tests/flips.cc
Original file line number Diff line number Diff line change
@@ -1,69 +1,107 @@
#include <chrono>
#include <cvd/image.h>
#include <cvd/vision.h>
#include <random>

using CVD::Image;
using CVD::SubImage;
using CVD::ImageRef;
using CVD::SubImage;

Image<int> im(int x, int y, const std::initializer_list<int>& data){
Image<int> im(int x, int y, const std::initializer_list<int>& data)
{
if(ImageRef(x, y).area() != (int)data.size())
abort();

return SubImage<int>(const_cast<int*>(std::data(data)), ImageRef(x, y));
}

int main(){

int main()
{

Image<int> a;

a = im(2,2,
{
1, 2,
3, 4
});
a = im(2, 2,
{ 1, 2,
3, 4 });

flipVertical(a);

if(!std::equal(a.begin(), a.end(), im(2,2,
{
3, 4,
1, 2
}).begin()))
if(!std::equal(a.begin(), a.end(), im(2, 2, { 3, 4, 1, 2 }).begin()))
throw std::logic_error("Even sized flipV failed");


a = im(2,3,
{
1, 2,
3, 4,
5, 6
});
////////////////////////////////////////////////////////////////////////////////
a = im(2, 3,
{ 1, 2,
3, 4,
5, 6 });

flipVertical(a);

if(!std::equal(a.begin(), a.end(), im(2,3,
{
5, 6,
3, 4,
1, 2
}).begin()))
if(!std::equal(a.begin(), a.end(), im(2, 3, { 5, 6, 3, 4, 1, 2 }).begin()))
throw std::logic_error("Odd sized flipV failed");

////////////////////////////////////////////////////////////////////////////////

a = im(2,3,
{
1, 2,
3, 4,
5, 6
});
a = im(2, 3,
{ 1, 2,
3, 4,
5, 6 });

a = CVD::Internal::simpleTranspose(a);
Image<int> b(a.size().transpose());
CVD::Internal::simpleTranspose(a, b);

if(!std::equal(a.begin(), a.end(), im(2,3,
{
1, 3, 5,
2, 4, 6
}).begin()))
if(!std::equal(b.begin(), b.end(), im(2, 3, { 1, 3, 5, 2, 4, 6 }).begin()))
throw std::logic_error("Simple transpose failed");

////////////////////////////////////////////////////////////////////////////////

a = im(2, 3,
{ 1, 2,
3, 4,
5, 6 });

b.resize(a.size().transpose());
CVD::Internal::recursiveTranspose(a, b, 1);

if(!std::equal(b.begin(), b.end(), im(2, 3, { 1, 3, 5, 2, 4, 6 }).begin()))
throw std::logic_error("Recursive transpose failed (small)");

////////////////////////////////////////////////////////////////////////////////
const int N = 100;
const int R = 10;

std::mt19937 eng;
for(int i = 0; i < N; i++)
{
std::uniform_int_distribution<> rng(1, 1024);
std::uniform_int_distribution<> byte(0, 255);

ImageRef size;
size.x = rng(eng);
size.y = rng(eng);

double s_simple = 0, s_recursive = 0;

for(int j = 0; j < R; j++)
{
Image<uint8_t> im1(size), im2;
for(auto& p : im1)
p = static_cast<uint8_t>(byte(eng));

auto t1 = std::chrono::steady_clock::now();
Image<uint8_t> recurs = CVD::transpose(im1);
auto t2 = std::chrono::steady_clock::now();

Image<uint8_t> simple(recurs.size());
CVD::Internal::simpleTranspose(im1, simple);
auto t3 = std::chrono::steady_clock::now();

if(!std::equal(simple.begin(), simple.end(), recurs.begin()))
throw std::logic_error("Recursive transpose failed");

s_recursive += std::chrono::duration<double>(t2 - t1).count();
s_simple += std::chrono::duration<double>(t3 - t2).count();
}
std::cout << size << " " << size.area() << " " << s_simple / R << " " << s_recursive / R << "\n";
}
}

0 comments on commit db57afe

Please sign in to comment.