Commit af4ff639 authored by Michaël Roynard's avatar Michaël Roynard
Browse files

Add code snippets.

parent f92c534d
# We'll use defaults from the LLVM style, but with 4 columns indentation.
BasedOnStyle: LLVM
IndentWidth: 2
---
Language: Cpp
# Force pointers to the type for C++.
DerivePointerAlignment: false
PointerAlignment: Left
# 120 cols
ColumnLimit: 120
Cpp11BracedListStyle: true
# break brace style
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
NamespaceIndentation: All
FixNamespaceComments: true
IndentWrappedFunctionNames: false # Do not indent after function def break
AllowShortFunctionsOnASingleLine: Inline
AlwaysBreakTemplateDeclarations: true
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: true
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
MaxEmptyLinesToKeep: 2
# For loop
ForEachMacros: ['mln_forall', 'mln_foreach', 'mln_foreach_new', 'mln_reverse_foreach']
build
\ No newline at end of file
project(icip-2020)
cmake_minimum_required(VERSION 3.14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall")
add_executable(test
src/dilate.cpp
src/algorithms.cpp
src/image.cpp
src/main.cpp
include/dilate.hpp
include/types.hpp)
target_include_directories(test PRIVATE include)
target_compile_features(test PRIVATE cxx_std_20)
target_compile_options(test PRIVATE -fconcepts)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -march=native -g -Wno-strict-aliasing")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(test PRIVATE TESTING)
endif()
#pragma once
#include "transform.hpp"
#include "types.hpp"
static inline auto togray_f = [](rgb16 x) -> uint16_t { return static_cast<uint16_t>((x.r + x.g + x.b) / 3); };
static inline auto subquant_f = [](uint16_t x) -> uint8_t { return static_cast<uint8_t>(x / 256); };
void togray(image2d<rgb16> input, image2d<uint16_t> output, rect roi);
void subquantize8(image2d<uint16_t> input, image2d<uint8_t> output, rect roi);
template <Image2D I>
auto togray_view(I input)
{
return transform(input, togray_f);
}
template <Image2D I>
auto subquantize8_view(I input)
{
return transform(input, subquant_f);
}
#pragma once
#include "types.hpp"
template <class U, class V, class F>
void apply(U in, V out, rect roi, F f)
{
int y1 = roi.y + roi.height;
int x1 = roi.x + roi.width;
for (int y = roi.y; y < y1; ++y)
for (int x = roi.x; x < x1; ++x)
out({x, y}) = f(in({x, y}));
}
template <Image2D U, class T>
void copy_to(U in, image2d<T> out, rect roi)
{
for (int y = 0; y < roi.height; ++y)
for (int x = 0; x < roi.width; ++x)
out({x, y}) = in({roi.x + x, roi.y + y});
}
#pragma once
#include "apply.hpp"
#include "types.hpp"
class TileLoaderBase
{
public:
static constexpr int BLOCK_SIZE = 128;
virtual image2d<uint8_t> load(rect roi) = 0;
};
class TileLoaderCache : public TileLoaderBase
{
public:
TileLoaderCache();
virtual ~TileLoaderCache();
TileLoaderCache(const TileLoaderCache&) = delete;
TileLoaderCache& operator=(const TileLoaderCache&) = delete;
TileLoaderCache(TileLoaderCache&&) = delete;
TileLoaderCache& operator=(TileLoaderCache&&) = delete;
protected:
image2d<uint8_t> m_tile;
};
template <class I>
class TileLoader : public TileLoaderCache
{
public:
TileLoader(I input)
: m_input(input)
{
}
image2d<uint8_t> load(rect roi) final
{
// Because of the border
roi.x -= 1;
roi.y -= 1;
roi.width += 2;
roi.height += 2;
copy_to(m_input, m_tile, roi);
return m_tile.shift({1, 1});
}
private:
I m_input;
};
template <>
class TileLoader<image2d<uint8_t>> : public TileLoaderBase
{
public:
TileLoader(image2d<uint8_t> input)
: m_input(input)
{
}
image2d<uint8_t> load(rect roi) final { return m_input.shift({roi.x, roi.y}); }
private:
image2d<uint8_t> m_input;
};
namespace details
{
void dilate8(TileLoaderBase* loader, image2d<uint8_t> out, rect roi);
}
template <class I>
void dilate8(I input, image2d<uint8_t> out, rect roi)
{
TileLoader<I> loader(input);
details::dilate8(&loader, out, roi);
}
#pragma once
#include "types.hpp"
template <class T>
image2d<T> create_image(int width, int height);
void destroy_image(image2d<void> f);
template <class T>
void fill_with_random_values(image2d<T> out, rect roi);
/******************************************/
/**** Implementation ****/
/******************************************/
namespace details
{
void create_image(int width, int height, std::size_t size, image2d<void>& out);
void fill_with_random_values(image2d<void> out, std::size_t bsize, rect roi);
} // namespace details
template <class T>
image2d<T> create_image(int width, int height)
{
image2d<T> out;
details::create_image(width, height, sizeof(T), out);
return out;
}
template <class T>
void fill_with_random_values(image2d<T> out, rect roi)
{
details::fill_with_random_values(out, sizeof(T), roi);
}
#pragma once
#include "types.hpp"
#include <utility>
template <class I, class F>
struct transformed_image_t
{
auto operator()(point p) { return m_fun(m_input(p)); }
auto operator()(point p) const { return m_fun(m_input(p)); }
I m_input;
F m_fun;
};
template <Image2D I, class F>
auto transform(I input, F fun) -> transformed_image_t<I, F>
{
return {std::move(input), std::move(fun)};
}
#pragma once
#include <cstddef>
#include <cstdint>
#include <type_traits>
struct rect
{
int x;
int y;
int width;
int height;
};
struct point
{
int x;
int y;
};
struct rgb16
{
uint16_t r, g, b;
};
template <class T>
struct image2d;
template <>
struct image2d<void>
{
std::byte* m_buffer;
std::ptrdiff_t m_byte_stride;
};
template <class I>
concept Image2D = std::is_invocable_v<I, point>;
template <class T>
struct image2d : image2d<void>
{
T& operator()(point p) const
{
T* lineptr = (T*)((char*)m_buffer + m_byte_stride * p.y);
return lineptr[p.x];
}
image2d<T> shift(point p) const
{
image2d<T> tmp;
tmp.m_buffer = m_buffer + m_byte_stride * p.y + sizeof(T) * p.x;
tmp.m_byte_stride = m_byte_stride;
return tmp;
}
T* buffer() const { return (T*)m_buffer; }
std::ptrdiff_t stride() const { return m_byte_stride / sizeof(T); }
};
#include "algorithms.hpp"
#include "apply.hpp"
void togray(image2d<rgb16> input, image2d<uint16_t> output, rect roi)
{
apply(input, output, roi, togray_f);
}
void subquantize8(image2d<uint16_t> input, image2d<uint8_t> output, rect roi)
{
apply(input, output, roi, subquant_f);
}
#include "dilate.hpp"
#include <algorithm>
namespace details
{
namespace
{
void dilate8(image2d<uint8_t> input, image2d<uint8_t> output, int width, int height)
{
uint8_t* __restrict A = input.buffer();
uint8_t* __restrict B = output.buffer();
for (int y = 0; y < height; ++y)
{
auto a = A - input.stride();
auto b = A;
auto c = A + input.stride();
for (int x = 0; x < width; ++x)
{
int m = b[x];
if (a[x - 1] > m)
m = a[x - 1];
if (a[x] > m)
m = a[x];
if (a[x + 1] > m)
m = a[x + 1];
if (b[x - 1] > m)
m = b[x - 1];
// if (b[x] > m) m = b[x];
if (b[x + 1] > m)
m = b[x + 1];
if (c[x - 1] > m)
m = c[x - 1];
if (c[x] > m)
m = c[x];
if (c[x + 1] > m)
m = c[x + 1];
B[x] = m;
}
A += input.stride();
B += output.stride();
}
}
} // namespace
void dilate8(TileLoaderBase* loader, image2d<uint8_t> out, rect roi)
{
for (int y = 0; y < roi.height; y += TileLoaderBase::BLOCK_SIZE)
for (int x = 0; x < roi.width; x += TileLoaderBase::BLOCK_SIZE)
{
int w = std::min(roi.width - x, TileLoaderBase::BLOCK_SIZE);
int h = std::min(roi.height - y, TileLoaderBase::BLOCK_SIZE);
rect r = {roi.x + x, roi.y + y, w, h};
auto tile_in = loader->load(r);
auto tile_out = out.shift({x, y});
dilate8(tile_in, tile_out, w, h);
}
}
} // namespace details
TileLoaderCache::TileLoaderCache()
{
int sz = TileLoaderBase::BLOCK_SIZE + 2;
m_tile.m_buffer = (std::byte*)std::malloc(sz * sz * sizeof(uint8_t));
m_tile.m_byte_stride = sz;
}
TileLoaderCache::~TileLoaderCache()
{
std::free(m_tile.m_buffer);
}
#include "image.hpp"
#include <cstdlib>
namespace details
{
void create_image(int width, int height, std::size_t size, image2d<void>& out)
{
out.m_buffer = (std::byte*)std::malloc(width * height * size);
out.m_byte_stride = width * size;
}
void fill_with_random_values(image2d<void> out, std::size_t bsize, rect roi)
{
int y1 = roi.y + roi.height;
int x0 = roi.x * bsize;
int x1 = (roi.x + roi.width) * bsize;
for (int y = roi.y; y < y1; ++y)
{
uint8_t* lineptr = (uint8_t*)out.m_buffer + out.m_byte_stride * y;
for (int x = x0; x < x1; ++x)
lineptr[x] = uint8_t(std::rand());
}
}
} // namespace details
void destroy_image(image2d<void> f)
{
std::free(f.m_buffer);
}
#include "algorithms.hpp"
#include "dilate.hpp"
#include "image.hpp"
#include <cassert>
#include <chrono>
#include <iostream>
void chain1(image2d<rgb16> input, image2d<uint8_t> output, rect roi)
{
auto A = create_image<uint16_t>(roi.width, roi.height);
auto B = create_image<uint8_t>(roi.width, roi.height);
togray(input, A, roi);
subquantize8(A, B, roi);
roi.x += 1;
roi.y += 1;
roi.width -= 2;
roi.height -= 2;
dilate8(B, output, roi);
destroy_image(A);
destroy_image(B);
}
void chain2(image2d<rgb16> input, image2d<uint8_t> output, rect roi)
{
auto A = togray_view(input);
auto B = subquantize8_view(A);
roi.x += 1;
roi.y += 1;
roi.width -= 2;
roi.height -= 2;
dilate8(B, output, roi);
}
void test(image2d<rgb16> input, rect roi, int width, int height)
{
std::cout << "Testing output...";
auto output1 = create_image<uint8_t>(width, height);
chain1(input, output1, roi);
auto output2 = create_image<uint8_t>(width, height);
chain1(input, output2, roi);
bool same = true;
for (int x = 0; x < width; ++x)
for (int y = 0; y < height; ++y)
same &= output1({x, y}) == output2({x, y});
assert(same);
std::cout << " OK" << std::endl;
}
int main()
{
int width = 5160;
int height = 3872;
rect roi = {0, 0, width, height};
auto input = create_image<rgb16>(width, height);
auto output = create_image<uint8_t>(width, height);
fill_with_random_values(input, roi);
#ifdef TESTING
test(input, roi, width, height);
#endif
std::chrono::duration<double> d1, d2;
{
auto start = std::chrono::system_clock::now();
chain1(input, output, roi);
auto end = std::chrono::system_clock::now();
d1 = end - start;
}
{
auto start = std::chrono::system_clock::now();
chain2(input, output, roi);
auto end = std::chrono::system_clock::now();
d2 = end - start;
}
destroy_image(input);
destroy_image(output);
std::cout << "Normal pipeline: " << d1.count() << "s\n"
<< "View pipeline: " << d2.count() << "s\n";
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment