diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b26fa5..6252bed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,34 +1,39 @@ cmake_minimum_required (VERSION 3.1) -project(color) +project(perception) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wconversion -Wno-unused-private-field -Wno-unused-variable -Wno-unused-parameter -fno-omit-frame-pointer -fsanitize=address") set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_STATIC_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wconversion -Wno-unused-private-field -Wno-unused-variable -Wno-unused-parameter") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") +# set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") find_package(PythonLibs REQUIRED 3) -find_package(PythonInterp REQUIRED 3) -find_package(NumPy REQUIRED) +# find_package(PythonInterp REQUIRED 3) +# find_package(NumPy REQUIRED) -add_library(color SHARED src/debug.cpp src/conversion.cpp src/CIEDE2000.cpp src/fitness.cpp) -target_include_directories(color PUBLIC include PRIVATE ${PYTHON_INCLUDE_DIR} ${NUMPY_INCLUDE_DIR}) +add_library(perception SHARED src/colorspace.cpp src/CIEDE2000.cpp src/fitness.cpp) +target_include_directories(perception PUBLIC include) -add_executable(testCIEDE2000 test/testCIEDE2000.cpp) -target_link_libraries(testCIEDE2000 color) -add_executable(testconversion test/testconversion.cpp) -target_link_libraries(testconversion color) -add_executable(testfitness test/testfitness.cpp) -target_link_libraries(testfitness color) -add_executable(testfitness2 test/testfitness2.cpp) -target_link_libraries(testfitness2 color) +add_library(pyperception MODULE src/python.cpp) +target_link_libraries(pyperception perception ${PYTHON_LIBRARIES}) +target_include_directories(pyperception PRIVATE include ${PYTHON_INCLUDE_DIR}) +set_target_properties(pyperception PROPERTIES PREFIX "") -add_executable(evo1 test/example1.cpp) -target_link_libraries(evo1 color) -add_executable(evo2 test/example2.cpp) -target_link_libraries(evo2 color) +add_executable(testevo1 test/example1.cpp) +target_link_libraries(testevo1 perception) +add_executable(testevo2 test/example2.cpp) +target_link_libraries(testevo2 perception) add_executable(testlexi test/testlexi.cpp) -target_link_libraries(testlexi color) \ No newline at end of file +target_link_libraries(testlexi perception) + +add_executable(testCIEDE2000 test/testCIEDE2000.cpp) +target_link_libraries(testCIEDE2000 perception) +add_executable(testcolorspace test/testcolorspace.cpp) +target_link_libraries(testcolorspace perception) +add_executable(testfitness test/testfitness.cpp) +target_link_libraries(testfitness perception) +add_executable(testfitness2 test/testfitness2.cpp) +target_link_libraries(testfitness2 perception) \ No newline at end of file diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 74c1ab7..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Greg Fiumara - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/include/color.h b/include/color.h index 10fd456..daddc60 100644 --- a/include/color.h +++ b/include/color.h @@ -4,8 +4,7 @@ * See LICENSE for details. */ -#ifndef GPF_COLOR_H_ -#define GPF_COLOR_H_ +#pragma once #include @@ -62,8 +61,6 @@ RGB LABtoRGB(const LAB &lab); } // namespace color -std::ostream &operator<<(std::ostream &s, const color::LAB &labColor); -std::ostream &operator<<(std::ostream &s, const color::XYZ &labColor); -std::ostream &operator<<(std::ostream &s, const color::RGB &labColor); - -#endif /* GPF_COLOR_H_ */ +std::ostream &operator<<(std::ostream &s, const color::LAB &lab); +std::ostream &operator<<(std::ostream &s, const color::XYZ &xyz); +std::ostream &operator<<(std::ostream &s, const color::RGB &rgb); diff --git a/include/constant.h b/include/constant.h index f1ba761..76c944a 100644 --- a/include/constant.h +++ b/include/constant.h @@ -1,5 +1,4 @@ -#ifndef ICC_CONSTANT_H_ -#define ICC_CONSTANT_H_ +#pragma once namespace color { /* ICC's PCS illuminant */ @@ -17,6 +16,4 @@ const double A23 = -0.03294208; const double A31 = -0.03269638; const double A32 = -0.18259425; const double A33 = 1.4716528; -} // namespace color - -#endif /* ICC_CONSTANT_H_ */ \ No newline at end of file +} // namespace color \ No newline at end of file diff --git a/include/fitness.h b/include/fitness.h index 151a8ca..a91517a 100644 --- a/include/fitness.h +++ b/include/fitness.h @@ -1,5 +1,4 @@ -#ifndef FITNESS_H_ -#define FITNESS_H_ +#pragma once #include "color.h" #include "lexi.h" @@ -14,25 +13,13 @@ LexiProduct fitnessFunc(const std::vector &lab); class PerceptionResult { public: - friend std::ostream &operator<<(std::ostream &os, - const PerceptionResult &res) { - os << "{" << std::endl << " flags: " << res.flags << std::endl; - os << " L: " << res.L << std::endl; - os << " rgb: [" << std::endl; - for (const auto &rgb : res.rgb) - os << " 0: " << rgb << std::endl; - os << " ]" << std::endl; - os << " fitness: " << res.fitness << std::endl << "}"; - return os; - } - unsigned long flags; double L; std::vector rgb; LexiProduct fitness; }; -PerceptionResult perceptionL(color::LAB foreground, color::LAB background, - size_t M, bool quiet = false); +std::ostream &operator<<(std::ostream &os, const PerceptionResult &res); -#endif \ No newline at end of file +PerceptionResult perceptionL(color::LAB foreground, color::LAB background, + size_t M, bool quiet = false); \ No newline at end of file diff --git a/include/lexi.h b/include/lexi.h index 863ef34..a9461db 100644 --- a/include/lexi.h +++ b/include/lexi.h @@ -1,5 +1,4 @@ -#ifndef LEXI_PRODUCT_H_ -#define LEXI_PRODUCT_H_ +#pragma once #include #include @@ -89,6 +88,4 @@ template class LexiProduct { } return lencmp; } -}; - -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/src/CIEDE2000.cpp b/src/CIEDE2000.cpp index 0ac1d57..ccd7674 100644 --- a/src/CIEDE2000.cpp +++ b/src/CIEDE2000.cpp @@ -5,7 +5,6 @@ */ #include - #include /******************************************************************************* diff --git a/src/conversion.cpp b/src/colorspace.cpp similarity index 76% rename from src/conversion.cpp rename to src/colorspace.cpp index 13afb09..ed37572 100644 --- a/src/conversion.cpp +++ b/src/colorspace.cpp @@ -43,3 +43,15 @@ XYZ LABtoXYZ(const LAB &lab) { RGB LABtoRGB(const LAB &lab) { return XYZtoRGB(LABtoXYZ(lab)); } } // namespace color + +std::ostream &operator<<(std::ostream &s, const color::LAB &lab) { + return (s << "LAB(" << lab.l << "," << lab.a << "," << lab.b << ")"); +} + +std::ostream &operator<<(std::ostream &s, const color::XYZ &xyz) { + return (s << "XYZ(" << xyz.x << "," << xyz.y << "," << xyz.z << ")"); +} + +std::ostream &operator<<(std::ostream &s, const color::RGB &rgb) { + return (s << "RGB(" << rgb.r << "," << rgb.g << "," << rgb.b << ")"); +} diff --git a/src/debug.cpp b/src/debug.cpp deleted file mode 100644 index b066371..0000000 --- a/src/debug.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include - -std::ostream &operator<<(std::ostream &s, const color::LAB &lab) { - return (s << "LAB(" << lab.l << "," << lab.a << "," << lab.b << ")"); -} - -std::ostream &operator<<(std::ostream &s, const color::XYZ &xyz) { - return (s << "XYZ(" << xyz.x << "," << xyz.y << "," << xyz.z << ")"); -} - -std::ostream &operator<<(std::ostream &s, const color::RGB &rgb) { - return (s << "RGB(" << rgb.r << "," << rgb.g << "," << rgb.b << ")"); -} \ No newline at end of file diff --git a/src/fitness.cpp b/src/fitness.cpp index c88327b..553e0c0 100644 --- a/src/fitness.cpp +++ b/src/fitness.cpp @@ -36,7 +36,7 @@ LexiProduct fitnessFunc(const std::vector &lab) { } // number of combinations between colors, except the last two - size_t K = M * (M - 1) / 2 - 1; + size_t K = M * (M - 1) / 2 - 1; static size_t cached_K = 0; static std::vector cached_ref; /* for almost-sorted O(n) sorting */ static std::vector> ref_combi; @@ -87,6 +87,17 @@ void fill_lab(const std::vector &x, std::vector &lab, } } +std::ostream &operator<<(std::ostream &os, const PerceptionResult &res) { + os << "{" << std::endl << " flags: " << res.flags << std::endl; + os << " L: " << res.L << std::endl; + os << " rgb: [" << std::endl; + for (const auto &rgb : res.rgb) + os << " " << rgb << std::endl; + os << " ]" << std::endl; + os << " fitness: " << res.fitness << std::endl << "}"; + return os; +} + /* * @param foreground * @param background @@ -133,11 +144,12 @@ PerceptionResult perceptionL(color::LAB foreground, color::LAB background, const auto xfinal = evo.getVector(CMAES>::XMean); fill_lab(xfinal, lab, M); - std::sort(lab.begin(), lab.begin() + (long)M, [](const color::LAB &c1, const color::LAB &c2) { - const double h1 = std::atan2(-c1.b, -c1.a); - const double h2 = std::atan2(-c2.b, -c2.a); - return h1 < h2; - }); + std::sort(lab.begin(), lab.begin() + (long)M, + [](const color::LAB &c1, const color::LAB &c2) { + const double h1 = std::atan2(-c1.b, -c1.a); + const double h2 = std::atan2(-c2.b, -c2.a); + return h1 < h2; + }); auto fitness = evo.getValue(CMAES>::FBestEver); if (!quiet) diff --git a/src/python.cpp b/src/python.cpp new file mode 100644 index 0000000..8198760 --- /dev/null +++ b/src/python.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +//< RAII wrapper for PyObject +class PyObj { +public: + PyObj() = delete; + PyObj(PyObject *ptr) : ptr(ptr){}; + PyObj(const PyObj &obj) : ptr(obj.ptr) { Py_XINCREF(obj.ptr); }; + PyObj(PyObj &&obj) : ptr(obj.ptr) { obj.ptr = nullptr; }; + PyObj &operator=(const PyObj &obj) { + ptr = obj.ptr; + Py_XINCREF(ptr); + return *this; + }; + PyObj &operator=(PyObj &&obj) { + ptr = obj.ptr; + obj.ptr = nullptr; + return *this; + }; + ~PyObj() { Py_XDECREF(ptr); }; + + PyObject *ptr; +}; + +static PyObject *perception(PyObject *self, PyObject *args) { + color::LAB fg, bg; + unsigned long M; + bool quiet; + + if (!PyArg_ParseTuple(args, "(ddd)(ddd)k|p", &fg.l, &fg.a, &fg.b, &bg.l, + &bg.a, &bg.b, &M, &quiet)) + return NULL; + + const auto result = perceptionL(fg, bg, M, quiet); + PyObj rgb = PyObj(PyList_New(0)); + for (const auto &c : result.rgb) + PyList_Append(rgb.ptr, PyObj(Py_BuildValue("(ddd)", c.r, c.g, c.b)).ptr); + PyObj fitness = PyObj(PyList_New(0)); + for (const auto d : result.fitness.prd) + PyList_Append(fitness.ptr, PyObj(PyFloat_FromDouble(d)).ptr); + + return Py_BuildValue("{sksdsOsO}", "flags", result.flags, "L", result.L, + "rgb", rgb.ptr, "fitness", fitness.ptr); +} + +static PyMethodDef methods[] = { + {"perception", perception, METH_VARARGS, + "perception colors\n-----------------\n\n@param foreground " + "(l,a,b)\n@param background (l,a,b)\n@param M number of colors\n@param " + "quiet default:False"}, + {NULL, NULL, 0, NULL}}; + +static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, "pyperception", + "Python interface for perception colors library", -1, methods}; + +PyMODINIT_FUNC PyInit_pyperception(void) { return PyModule_Create(&module); } \ No newline at end of file diff --git a/test/testCIEDE2000.cpp b/test/testCIEDE2000.cpp index 8f80bb7..a0cbd15 100644 --- a/test/testCIEDE2000.cpp +++ b/test/testCIEDE2000.cpp @@ -5,12 +5,11 @@ */ #include +#include #include #include #include -#include - /** * @brief * Run the test dataset run from the color paper. diff --git a/test/testconversion.cpp b/test/testcolorspace.cpp similarity index 99% rename from test/testconversion.cpp rename to test/testcolorspace.cpp index 711b008..3ca4b00 100644 --- a/test/testconversion.cpp +++ b/test/testcolorspace.cpp @@ -5,12 +5,11 @@ */ #include +#include #include #include #include -#include - static size_t line = 0; static std::vector passFail; diff --git a/test/testfitness.cpp b/test/testfitness.cpp index 9a5dbff..53332d8 100644 --- a/test/testfitness.cpp +++ b/test/testfitness.cpp @@ -1,17 +1,11 @@ -/* - * testcolor.cpp - * Part of http://github.com/gfiumara/color by Gregory Fiumara. - * See LICENSE for details. - */ - #include +#include +#include +#include #include #include #include -#include -#include - static size_t line = 0; static std::vector passFail; @@ -23,6 +17,7 @@ void check(bool res, const std::string &msg) { } void check(double a, double b, const std::string &msg, double delta = 1e-3) { + // std::cout << std::setprecision(10) << a << " " << b << std::endl; check(std::abs(a - b) < delta, msg); } @@ -50,9 +45,9 @@ int testcolor() { {50, 10, -10}})[0], -12.8001, "fitnessFunc"); - check( - fitnessFunc(std::vector{{50, -200, -200}, {50, 200, 200}})[0], - 1626.445599, "fitnessFunc (off boundary)"); + check(fitnessFunc(std::vector{ + {50, -200, -200}, {50, 200, 200}, {50, 0, 0}, {50, 0, 0}})[0], + 3337.715201, "fitnessFunc (off boundary)"); std::cout << std::endl; int ret = EXIT_SUCCESS; diff --git a/test/testfitness2.cpp b/test/testfitness2.cpp index 7524cf3..9eda9dc 100644 --- a/test/testfitness2.cpp +++ b/test/testfitness2.cpp @@ -1,13 +1,12 @@ -#include -#include -#include -#include - #include +#include #include #include #include +#include #include +#include +#include unsigned compress(double x, unsigned max = 256) { return std::min((unsigned)std::max(0., x * max), max - 1); @@ -21,22 +20,22 @@ int main(int argc, char *argv[]) { std::cout << result << std::endl; // Write a HTML demo - const color::RGB frgb(color::LABtoRGB(foreground)); - const color::RGB brgb(color::LABtoRGB(background)); - std::ofstream myfile; - myfile.open("index.html"); - myfile << ""; - myfile << "
"; - for (const auto &rgb : result.rgb) { - myfile << "
"; - } - myfile << ""; - myfile.close(); + // const color::RGB frgb(color::LABtoRGB(foreground)); + // const color::RGB brgb(color::LABtoRGB(background)); + // std::ofstream myfile; + // myfile.open("index.html"); + // myfile << ""; + // myfile << "
"; + // for (const auto &rgb : result.rgb) { + // myfile << "
"; + // } + // myfile << ""; + // myfile.close(); return 0; } \ No newline at end of file