Skip to content

Instantly share code, notes, and snippets.

@duckie
Last active January 1, 2016 03:38
Show Gist options
  • Save duckie/8086422 to your computer and use it in GitHub Desktop.
Save duckie/8086422 to your computer and use it in GitHub Desktop.
C++11 - Associate a RGB color to a given unsigned integral number in order to maximize local contrast. Two algorithms are given. The second is called in the main loop. With no arguments, a consistency check verifies that every possible color in the context are used, with no overlap. With 1 arguments, it outputs the colors from index 0 to ARG (in…
#include <array>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <tuple>
#include <set>
#include <map>
#include <vector>
namespace colorscale {
using uchar_t = unsigned char;
using color_t = std::array<uchar_t, 3>;
color_t ComputeColorFromIndexSimple(size_t index) {
color_t result {{0,0,0}};
for(size_t i = 0; i < 8; ++i) {
size_t power = 7 - i;
for(uchar_t& elem : result) {
elem |= ((index%2) << power);
index /= 2;
}
}
return result;
}
// First element is the abcissa, second is the direction
// Third element is the ordinate, 4th is the direction
// 5th tells if the last dimension must be set to 0 (false) or 255 (true)
using range_t = std::tuple<uchar_t, bool, uchar_t, bool, bool>;
using cube_8th_t = std::array<range_t, 3>;
color_t ComputeColorFromIndex(size_t index) {
// Defining constants data describing the RGB cube face parts
std::array<color_t, 8> const summits {{
{{0,0,0}}
, {{255,0,0}}
, {{255,255,0}}
, {{0,255,0}}
, {{0,0,255}}
, {{255,0,255}}
, {{255,255,255}}
, {{0,255,255}}
}};
std::array<cube_8th_t, 8> const faces_parts {{
{{ range_t(0,true,2,true, false), range_t(2,true,1,true,false), range_t(1,true,0,true,false) }}
, {{ range_t(1,true,2,true,true), range_t(2,true,0,false,false), range_t(0,false,1,true,false) }}
, {{ range_t(0,false,2,true,true), range_t(2,true, 1, false, true), range_t(1,false,0,false, false) }}
, {{ range_t(1,false,2,true,false), range_t(2,true,0,true,true), range_t(0,true,1,false,false) }}
, {{ range_t(2,false,0,true,false), range_t(0,true,1,true,true), range_t(1,true,2,false,false) }}
, {{ range_t(2,false,1,true,true), range_t(1,true,0,false,true), range_t(0,false,2,false,false) }}
, {{ range_t(2,false,0,false,true), range_t(0,false,1,false,true), range_t(1,false,2,false,true) }}
, {{ range_t(2,false,1,false,false), range_t(1,false,0,true,true), range_t(0,true,2,false,true) }}
}};
auto const choose_bucket = [](size_t i) -> size_t {
switch (i) {
case 0: return 2;
case 1: return 5;
case 2: return 0;
case 3: return 3;
case 4: return 6;
case 5: return 1;
case 6: return 4;
case 7:
default: return 7;
}
};
// This algorithm is limited to the external faces of the RGB cube
index %= (2*256*256 + 2*256*254 + 2*254*254);
// The first 3 bits chose the summit
size_t summit_index = index % 8;
index /= 8;
color_t result {{ 0, 0, 0 }};
if(0u == index) {
result = summits[summit_index];
}
else {
// Choose a face then normalize the index
--index;
size_t chosen_face_index = index % 3;
index /= 3;
// Extract x component and y component
size_t x_comp = index%128;
size_t y_comp = index/128;
// The following lines work for 128x127 areas but is not
// easily extendable to other dimensions
size_t x_coord = 16*choose_bucket(x_comp%8) + x_comp/8;
size_t y_coord = 16*choose_bucket(y_comp%8) + y_comp/8 + 1;
// Project that position on the given face
range_t const & face = faces_parts[summit_index][chosen_face_index];
result[std::get<0>(face)] = std::get<1>(face) ? x_coord : 255 - x_coord;
result[std::get<2>(face)] = std::get<3>(face) ? y_coord : 255 - y_coord;
// Find the third axis
size_t sum = std::get<0>(face) + std::get<2>(face);
size_t third_axis = (1 == sum) ? 2u : ((2 == sum) ? 1u : 0u);
result[third_axis] = std::get<4>(face) ? 255u : 0u;
}
return result;
}
} // namespace colorscale
int main(int argc, char * argv[]) {
using colorscale::color_t;
using colorscale::ComputeColorFromIndex;
size_t index_start = 0;
size_t index_end = 390151;
if (2 < argc) {
index_start = ::atoi(argv[1]);
index_end = ::atoi(argv[2]);
}
else if (1 < argc) {
index_end = ::atoi(argv[1]);
}
// With no arguments, we do a consistency check
if(1 == argc) {
std::set<color_t> color_set;
for (size_t i = index_start; i <= index_end; ++i) {
color_set.insert(ComputeColorFromIndex(i));
if(0 == i%3900) std::cout << "\rConsistency check " << 100*(i-index_start)/(index_end-index_start) << " % " << std::flush;
}
std::cout << std::endl;
if(index_end + 1 == color_set.size()) {
std::cout << "Consistency test succeeded." << std::endl;
}
else {
std::cout << "Consistency test failed." << std::endl;
return 1;
}
}
else {
for (size_t i = index_start; i <= index_end; ++i) {
color_t color = ComputeColorFromIndex(i);
std::cout << static_cast<size_t>(color[0]) << ',' << static_cast<size_t>(color[1]) << ',' << static_cast<size_t>(color[2]) << std::endl;
}
}
return 0;
}
@duckie
Copy link
Author

duckie commented Dec 22, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment