pass parameter to boost integrator - c++

I have read these answers 1, 2 about passing parameter to odeint (Boost).
I tried here to do the same process. but the code give wrong answer.
Here is my try:
This is simplified code of boost calculation of lyapunov exponent of Lorenz oscillator.
#include <iostream>
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
#include "gram_schmidt.hpp"
using namespace std;
using namespace boost::numeric::odeint;
typedef vector<double> dim1;
const double sigma = 10.0;
const double R = 28.0;
const double b = 8.0 / 3.0;
const size_t n = 3;
const size_t num_of_lyap = 3;
const size_t N = n + n * num_of_lyap;
// system with out perturbation
struct lorenz
{
void operator()( const dim1 &x , dim1 &dxdt , double t ) const
{
dxdt[0] = sigma * ( x[1] - x[0] );
dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -b * x[2] + x[0] * x[1];
}
};
// system with perturbation
void lorenz_with_lyap( const dim1 &x , dim1 &dxdt , double t )
{
lorenz()( x , dxdt , t );
for( int l=0 ; l<num_of_lyap ; ++l )
{
const double *pert = &x[3 + l * 3];
double *dpert = &dxdt[3 + l * 3];
dpert[0] = - sigma * pert[0] + 10.0 * pert[1];
dpert[1] = ( R - x[2] ) * pert[0] - pert[1] - x[0] * pert[2];
dpert[2] = x[1] * pert[0] + x[0] * pert[1] - b * pert[2];
}
}
//------------------------------------------------------------------------
int main( int argc , char **argv )
{
const double dt = 0.01;
dim1 x(N);
x[0] = 10.; x[1] = 10.0; x[2] = 5.0; // initial condition
dim1 lyap(3);
runge_kutta4<dim1, double, dim1, double, range_algebra> rk4;
// perform 10000 transient steps
integrate_n_steps(rk4, lorenz(), x, 0.0, dt, 10000);
fill( x.begin()+n , x.end() , 0.0 );
for( size_t i=0 ; i<num_of_lyap ; ++i ) x[n+n*i+i] = 1.0;
fill( lyap.begin() , lyap.end() , 0.0 );
double t = 0.0;
size_t count = 0;
while( true )
{
t = integrate_n_steps( rk4 , lorenz_with_lyap , x , t , dt , 100 );
gram_schmidt< num_of_lyap >( x , lyap , n );
++count;
if( !(count % 1000) )
{
cout << t;
for( size_t i=0 ; i<num_of_lyap ; ++i ) cout << "\t" << lyap[i] / t ;
cout << endl;
}
}
return 0;
}
first I defined these classes
class lorenz
{
public:
void operator()...
and
class lorenz_with_lyap
{
public:
void operator()(const dim1 &x, dim1 &dxdt, double t) const
{
dxdt[0] = sigma * (x[1] - x[0]);
dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -b * x[2] + x[0] * x[1];
for (int l = 0; l < num_of_lyap; ++l)
{
const double *pert = &x[3 + l * 3];
double *dpert = &dxdt[3 + l * 3];
dpert[0] = -sigma * pert[0] + sigma * pert[1];
dpert[1] = (R - x[2]) * pert[0] - pert[1] - x[0] * pert[2];
dpert[2] = x[1] * pert[0] + x[0] * pert[1] - b * pert[2];
}
}
};
then I can change it a little to define a parameter:
class lorenz
{
double sigma;
public:
lorenz(double si) : sigma(si) { }
void operator()...
but defining the lorenz_with_lyap class change the result, and I don't know where I am doing something wrong.
Do you have any idea?
(This is now a homework and I am going to check the result of this code with another method).
Thank you.

After some struggling, I finally corrected that, post it here if somebody probably is interested:
class lorenz
{
public:
void operator()(const dim1 &x, dim1 &dxdt, double t) const
{
dxdt[0] = sigma * (x[1] - x[0]);
dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -b * x[2] + x[0] * x[1];
}
};
class lorenz_with_lyap
{
const double sig;
public:
lorenz_with_lyap(double si) : sig(si) { }
void operator()(const dim1 &x, dim1 &dxdt, double t) const
{
dxdt[0] = sig * (x[1] - x[0]);
dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
dxdt[2] = -b * x[2] + x[0] * x[1];
for (int l = 0; l < num_of_lyap; ++l)
{
const double *pert = &x[3 + l * 3];
double *dpert = &dxdt[3 + l * 3];
dpert[0] = -sig * pert[0] + 10.0 * pert[1];
dpert[1] = (R - x[2]) * pert[0] - pert[1] - x[0] * pert[2];
dpert[2] = x[1] * pert[0] + x[0] * pert[1] - b * pert[2];
}
}
};
//------------------------------------------------------------------------
// in main
lorenz_with_lyap lo(10.0);
t = integrate_n_steps(rk4, lo, x, t, dt, 100);
...

Related

4th order Runge Kutta method not working for coupled equations

I have to apply the 4th order Runge Kutta method (RK) to the coupled equation in handbook of marinecraft hydrodynamics and motion control, page 154, equation 7.33, to determine v and r for every iteration. the RK method works when I use the functions separately, i.e., when I use the functions in separate programs. I have converted the equations from matrix form to linear equations. q1, q2, q3 and q4 are coefficients of v and r
The RK method is given as a function and is called twice to find v and r values. I can obtain the correct range of values of r as when rud increases, the values of v and r increase as well. when rud = rudmax, v and r should remain constant. however, v keeps increasing and doesn't stop. both v and r should be constant when rud = rudmax.
#include <iostream>
#include <fstream>
#include <cmath>
#include <iomanip>
using namespace std;
double dvdt(double r, double v);
double drdt(double v, double r);
double RKv1(double to, double vo, double t, double h);
double RKr1(double to, double ro, double t, double h);
double xo = 0; //initial X position
double yo = 0; //initial Y position
double x1; //final X position
double y11; //final Y position
double ti = 0; //initial time = 0
double tf; //time entered
double dt = 0.01; //time step (iteration time)
double rud = 0; //rudder angle
double m = 3.27385 * pow(10, 11); //mass of submarine (Kg)
double Iz = 2.31448 * pow(10, 12); //moment of inertia of submarine (kg.m^2)
double xg = 10.3; //x position of COG
double yg = 0; // position of COG
double u = 10; // surge velocity
double v, vo = 0; //sway velocity
double r, ro = 0; // yaw rate
double psio = 0; //initial ship heading
double psi1; //final ship heading
double rudmax; // max rudder angle
double au; //surge acceleration
double av; // sway acceleration
double ar; //angular acceleration
double X; //surge force
double Y; //sway force
double N; //turning moment
double h = 1;
//double q1, q2, q3, q4;
//matrices
//maneuvering and hydrodynamic coefficents
double Xu1 = -0.010087715;//-1.0467 * pow(10,-3);
double Yv1 = -0.290828106;//-23.889 * pow(10, -3);
double Yr1 = -0.008070039;//-1.0510 * pow(10, -3);
double Nv1 = -0.002928156;// 1.1531 * pow(10, -3);
double Nr1 = -0.056559574; //-0.50717 * pow(10, -3);
double Xu = -0.007115651;// -2.8763 * pow(10, -3);
double Yv = -0.35098699; //-38.948 * pow(10, -3);
double Yr = 0.091748747;// 2.0031 * pow(10, -3);
double Nv = -0.123433077;//-14.287 * pow(10, -3);
double Nr = -0.056559574;//-4.2267 * pow(10, -3);
double Yrud = -0.587323034;//1.4308 * pow(10, -3);
double Nrud = 0.991135206;// -0.71540 * pow(10, -3);
double a1 = m - Yv1;
double b1 = m * xg - Yr1;
double a2 = m * xg - Yr1;
double b2 = Iz - Nr1;
double det = a1 * b2 - a2 * b1;
double q1 = (-Yv * (Iz - Nr1) / det);
double q2 = ((m - Xu1) * u - Yr) * (-m * xg + Yr1) / det;
double q3 = ((Xu1 - Yv1) * u - Nv) * (-m * xg + Yr1) / det;
double q4 = ((m * xg - Yr1) * u - Nr) * (m - Yr1) / det;
int main()
{
std::ofstream fout; //for .CSV file
fout.open("results2.csv", ios::out | ios::app); //for .CSV file
//cout << det<<"\n";
/*cout << "surge velocity = ";
cin >> */ u = 10;
/*cout << "rudmax = ";
cin >> */rudmax = 30;
/*cout << "final time = ";
cin >>*/ tf = 100;
fout << "rud" << ", " << "ti" << ", " << "r" << ", " << "v" << "\n"; //for .CSV file
while (ti <= tf)
{
v = RKv1(ti, vo, ti + 0.025, h);
r = RKr1(ti, ro, ti + 0.025, h);
fout << rud << ", " << ti << ", " << r << ", " << v << "\n"; //for .CSV file
vo = v;
ro = r;
ti = ti + dt;
if (rudmax > 0)
{
if (rud < rudmax)
{
rud = rud + 0.005;
}
}
else if (rudmax < 0)
{
if (rud > rudmax)
{
rud = rud - 0.005;
}
}
}
}
/*---------------------------------------------------FUNCTIONS---------------------------------------------------*/
//functions for velocity and distance
double dvdt(double r, double v)
{
//return(-Yrud * rud - ((-Yv * (Iz - Nr1) / det) * v + ((m - Xu1) * u - Yr) * ((-m * xg - Yr1) / det )* r));
return(-Yrud * rud - q1 * v + q2 * r);
}
double drdt(double v, double r)
{
//return(-Nrud * rud - (((Xu1 - Yv1) * u - Nv) * ((-m * xg + Yr) / det) * v + ((m * xg - Yr1) * u - Nr) * ((m - Yr1) / det) * r));
return(-Nrud * rud - q3 * v + q4 * r);
}
//RK methods
//correction of rk method
double RKv1(double to, double vo, double t, double h)
{
int n = /*(int)((t - to) / h)*/ 1;
double k1, k2, k3, k4;
double v = vo;
for (int i = 1; i <= n; i++)
{
k1 = h * dvdt(ro, v);
k2 = h * dvdt(ro, v + 0.5 * k1);
k3 = h * dvdt(ro, v + 0.5 * k2);
k4 = h * dvdt(ro, v + k3);
v = v + (1.0 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4);
to = to + h;
}
return v;
}
double RKr1(double to, double ro, double t, double h)
{
int n = /*(int)((t - to) / h)*/ 1;
double k1, k2, k3, k4;
double r = ro;
for (int i = 1; i <= n; i++)
{
k1 = h * drdt(vo, r);
k2 = h * drdt(vo, r + 0.5 * k1);
k3 = h * drdt(vo, r + 0.5 * k2);
k4 = h * drdt(vo, r + k3);
r = r + (1.0 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4);
to = to + h;
}
return r;
}
the results are stored in the results2.csv file.
snippet of the result. r (-28.59) remains constant whereas v (-84000) keep increasing
Any help/suggestions are appreciated. thanks!

multiphase, define profile

I have a question that I'm stuck in it;
I want to catch the VOF of the phases on the wall by a defined profile. Is there any macro for this purpose, and with what data accessing and looping macro?
I send my code here, so if you can help me, I will be appreciated. but when I apply my code, the fluent crashes out as soon as I initialize the solution.
DEFINE_PROFILE(heatflux_slip_shoulder_W, t, i)
{
/*Domain *d=Get_Domain(1);*/
int phase_domain_index = 0; /* primary phase index is 0 */
Thread *tm = THREAD_SUPER_THREAD(t);
Thread *subthread = THREAD_SUB_THREAD(tm,phase_domain_index);
double p[ND_ND]; /* this will hold the position vector */
double x, y, r, qslip, sigma, tav_stick, tav_fric, temp, Vx, Vy, Vz, V_W, V_T, VF;
double w = 1120;
double pi = 3.1415927;
double U = 0.002;
double press = 50000000;
double delta = 0.65;
double etta = 0.7;
double heat_ratio = 0.6383;
double Rshol = 0.0075;
double fric ;
double Rpin = 0.0025;
face_t f;
/*thread_loop_f(f, d)*/
{
begin_f_loop(f,t)
{
VF =F_VOF(f,subthread);
F_CENTROID(p, f, t);
x = p[0];
y = p[2];
r = sqrt((x*x) + (y*y));
temp = F_T(f,t);if ((297.<=temp)&&(temp<=273.))
{
sigma_6 = 325.80263157895 + (-0.171052631578952 * temp);
sigma_5 = 282.355263157897 + (-0.0921052631578974*temp);
}
...(and some conditions like above)...
if ((297.<=temp)&&(temp<=855.))
fric = 0.383752244165171 + (-0.000448833034111311 * temp);
else
fric = 0;
tav_6 = sigma_6*1000000/1.732;
tav_5 = sigma_5*1000000/1.732;
tav = VF * tav_6 + (1 - VF ) * tav_5;
qslip = ((pi*w*r/30) - (U*y/r)) * ((etta * (1 - delta) * tav) + (delta * fric * press));
F_PROFILE(f, t, i) = heat_ratio * qslip;
}
end_f_loop(f,t)
}
well, here is my code:
#include "udf.h"
#include "mem.h"
#include "sg.h"
#include "sg_mphase.h"
#include "flow.h"
#include "metric.h"
DEFINE_PROFILE(heatflux_slip_shoulder_W, t, i)
{
/*Domain *d=Get_Domain(1);*/
int phase_domain_index = 0; /* primary phase index is 0 */
Thread *tm = THREAD_SUPER_THREAD(t);
Thread *subthread = THREAD_SUB_THREAD(tm,phase_domain_index);
double p[ND_ND]; /* this will hold the position vector */
double x, y, r, qslip, sigma, tav_stick, tav_fric, temp, Vx, Vy, Vz, V_W, V_T, VF;
double w = 1120;
double pi = 3.1415927;
double U = 0.002;
double press = 50000000;
double delta = 0.65;
double etta = 0.7;
double heat_ratio = 0.6383;
double Rshol = 0.0075;
double fric ;
double Rpin = 0.0025;
face_t f;
/*thread_loop_f(f, d)*/
{
begin_f_loop(f,t)
{
VF =F_VOF(f,subthread);
F_CENTROID(p, f, t);
x = p[0];
y = p[2];
r = sqrt((x*x) + (y*y));
temp = F_T(f,t);if ((297.<=temp)&&(temp<=273.))
{
sigma_6 = 325.80263157895 + (-0.171052631578952 * temp);
sigma_5 = 282.355263157897 + (-0.0921052631578974*temp);
}
...(and some conditions like above)...
if ((297.<=temp)&&(temp<=855.))
fric = 0.383752244165171 + (-0.000448833034111311 * temp);
else
fric = 0;
tav_6 = sigma_6*1000000/1.732;
tav_5 = sigma_5*1000000/1.732;
tav = VF * tav_6 + (1 - VF ) * tav_5;
qslip = ((pi*w*r/30) - (U*y/r)) * ((etta * (1 - delta) * tav) + (delta * fric * press));
F_PROFILE(f, t, i) = heat_ratio * qslip;
}
end_f_loop(f,t)
}

Code compiles correctly but the image doesn't show up

I'm trying to test and animate some Clifford attractors
Seen here:
http://paulbourke.net/fractals/clifford/
I tried compiling the code taken from here.
http://paulbourke.net/fractals/clifford/paul_richards/main.cpp
The code compiles but no image file is generated.
I'm using:
Visual Studio Code
Version: 1.38.0
Commit: 3db7e09f3b61f915d03bbfa58e258d6eee843f35
Date: 2019-09-03T21:51:09.716Z
Electron: 4.2.10
Chrome: 69.0.3497.128
Node.js: 10.11.0
V8: 6.9.427.31-electron.0
OS: Linux x64 4.15.0-60-generic snap
/*
xn+1 = sin(a yn) + c cos(a xn)
yn+1 = sin(b xn) + d cos(b yn)
*/
#include <iostream>
#include <cmath>
#include <vector>
using namespace std;
// Change params only in this block
namespace {
const int width = 1600;
const int height = 1200;
const int frames = 10000;
const int iters = 10000;
const int skipIters = 10;
double sensitivity = 0.02;
const double minX = -4.0;
const double minY = minX * height / width;
const double maxX = 4.0;
const double maxY = maxX * height / width;
const double minA = acos( 1.6 / 2.0 );
const double maxA = acos( 1.3 / 2.0 );
const double minB = acos( -0.6 / 2.0 );
const double maxB = acos( 1.7 / 2.0 );
const double minC = acos( -1.2 / 2.0 );
const double maxC = acos( 0.5 / 2.0 );
const double minD = acos( 1.6 / 2.0 );
const double maxD = acos( 1.4 / 2.0 );
};
class Color {
public:
double r, g, b;
Color(const double &red = 0, const double &green = 0, const double &blue = 0) : r(red), g(green), b(blue) {
}
Color& operator+=(const Color &rhs) {
r += rhs.r;
g += rhs.g;
b += rhs.b;
return *this;
}
static Color createHue( double h ) {
h *= 6.0;
int hi = static_cast<int>( h );
double hf = h - hi;
switch( hi % 6 ) {
case 0:
return Color( 1.0 , hf, 0.0 );
case 1:
return Color( 1.0 - hf, 1.0, 0.0 );
case 2:
return Color( 0.0 , 1.0, hf );
case 3:
return Color( 0.0, 1.0 - hf, 1.0 );
case 4:
return Color( hf, 0.0, 1.0 );
case 5:
return Color( 1.0, 0.0, 1.0 - hf );
}
return Color();
}
Color operator+(const Color &rhs) const {
return Color(*this) += rhs;
}
};
int main(void) {
vector<Color> image( width * height );
for (int i = 0; i < frames; i++) {
const double p = static_cast<double>(i) / frames;
const double a = cos( minA + p * (maxA - minA) ) * 2.0;
const double b = cos( minB + p * (maxB - minB) ) * 2.0;
const double c = cos( minC + p * (maxC - minC) ) * 2.0;
const double d = cos( minD + p * (maxD - minD) ) * 2.0;
const Color curCol = Color::createHue( p );
double x = 0.0, y = 0.0;
for (int j = 0; j < iters; j++) {
double xn = sin(a * y) + c * cos(a * x);
double yn = sin(b * x) + d * cos(b * y);
x = xn;
y = yn;
if ( j < skipIters )
continue;
int xi = static_cast<int>( (x - minX) * width / (maxX - minX) );
int yi = static_cast<int>( (y - minY) * height / (maxY - minY) );
if ( xi >= 0 && xi < width &&
yi >= 0 && yi < height ) {
image[ xi + yi * width ] += curCol;
}
}
clog << "\r" << i;
}
clog << "\n";
cout
<< "P6\n"
<< width << " " << height << "\n"
<< "255\n";
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color &c = image[ x + y * width ];
unsigned char r = static_cast<unsigned char>( (1.0 - exp( -sensitivity * c.r )) * 255.0 );
unsigned char g = static_cast<unsigned char>( (1.0 - exp( -sensitivity * c.g )) * 255.0 );
unsigned char b = static_cast<unsigned char>( (1.0 - exp( -sensitivity * c.b )) * 255.0 );
cout << r << g << b;
}
}
return 0;
}

B-spline Curve in c++

Could anyone help me about B-spline Curve error?
I want to draw B-spline Curve in c++, but even though all coordinates are positive, the segment's coordinate is negative.
This is B-spline Curve code.
void BSplineCurve(Dot &ControlPoint1, Dot &ControlPoint2,
Dot &ControlPoint3,Dot &ControlPoint4,
Dot &DrawCurve, double &t){
double t2 = t * t;
double t3 = t2 * t;
double mt3 = (1 - t) * (1 - t) * (1 - t);
double bi3 = mt3 / 6;
double bi2 = ((3 * t3) - (6 * t2) + 4) / 6;
double bi1 = ((-3 * t3) + (3 * t2) + (3 * t) + 1) / 6;
double bi = mt3 / 6;
DrawCurve.x = ControlPoint1.x * bi3;
DrawCurve.x += ControlPoint2.x * bi2;
DrawCurve.x += ControlPoint3.x * bi1;
DrawCurve.x += ControlPoint4.x * bi;
DrawCurve.y = ControlPoint1.y * bi3;
DrawCurve.y += ControlPoint2.y * bi2;
DrawCurve.y += ControlPoint3.y * bi1;
DrawCurve.y += ControlPoint4.y * bi;
}
This is Drawing Code.
double t = 3.f;
do{
if ((3 < t) && (t <= 4)) {
BSplineCurve(ControlPoint1, ControlPoint2, ControlPoint3, ControlPoint4, DrawCurve, t);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
else if ((4 < t) && (t <= 5)) {
BSplineCurve(ControlPoint2, ControlPoint3, ControlPoint4, ControlPoint5, DrawCurve, t);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
else if ((5 < t) && (t <= 6)) {
BSplineCurve(ControlPoint3, ControlPoint4, ControlPoint5, ControlPoint6, DrawCurve, t);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
t += 0.001;
} while(t < 6.001);
This is Control Point's coordinate.
Poiont1 : 50, 50
Poiont2 : 50, 100
Poiont3 : 200, 100
Poiont4 : 200, 50
Poiont5 : 350, 50
Poiont6 : 350, 100
But this is 1st segment's coordinate.
Q3 : -1543, -349
Your drawing code looks wrong.
In function BSplineCurve the t parameter should take values in [0, 1] range. By changing t from 0 to 1 one will build a cubic B-spline between points ControlPoint2 and ControlPoint3.
You could try something like:
Dot points[6] = {ControlPoint1, ControlPoint2, ControlPoint3, ControlPoint4, ControlPoint5, ControlPoint6};
for(double t = 3.0; t < 6.0; t += 0.001)
{
const int start = static_cast<int>(t);
BSplineCurve(points[start - 3],
points[start - 2],
points[start - 1],
points[start],
DrawCurve,
t - start);
Draw1Dot(DrawCurve.x, DrawCurve.y, DrawCurve.R, DrawCurve.G, DrawCurve.B);
}
Update
Your B-spline calculation code looks wrong too :-)
bi should be t3/6.0 and not mt3/6.0. See here (slide 25).
The fixed function can look something like this (I did not test it):
void BSplineCurve(const Dot &point1,
const Dot &point2,
const Dot &point3,
const Dot &point4,
const double t,
Dot &result)
{
const double t2 = t * t;
const double t3 = t2 * t;
const double mt = 1.0 - t;
const double mt3 = mt * mt * mt;
const double bi3 = mt3;
const double bi2 = 3 * t3 - 6 * t2 + 4;
const double bi1 =-3 * t3 + 3 * t2 + 3 * t + 1;
const double bi = t3;
result.x = point1.x * bi3 +
point2.x * bi2 +
point3.x * bi1 +
point4.x * bi;
result.x /= 6.0;
result.y = point1.y * bi3 +
point2.y * bi2 +
point3.y * bi1 +
point4.y * bi;
result.y /= 6.0;
}
Maybe the point you use is too very close. In spline it is not good iidea to use very close point. becaue so we have very "galloping" curve. Like this:
red is original

Image retrieval system by Colour from the web using C++ with openframeworks

I am writing a program in C++ and openFrameworks that should hopefully implement an image retrieval system by colour matching. I have got an algorithm to find the match in a database by an rgb value. For example, if I have a database of 1000 pictures on my computer and I have a query rgb value 255,0,0 the program would look through 1000 pictures and find the closest match. However, my problem is that I want it to also look for the match on the web. I have been trying to find how to get images from websites, however, if you don't know the specific url of the image it's hard to get hold of the data. Maybe somebody has got some knowledge of how to get hold of images on websites? Ideally, the program would go on specified website and search through every webpage for the images, it would then compare each image to the query and output the closest match.
As I mentioned in my comment, it's a matter of converting from RGB colourspace to Lab* colourspace and using the euclidean distance to the average colour of the image from the database.
Here's a basic demo:
#include "testApp.h"
//ported from http://cookbooks.adobe.com/post_Useful_color_equations__RGB_to_LAB_converter-14227.html
struct Color{
float R,G,B,X,Y,Z,L,a,b;
};
#define REF_X 95.047; // Observer= 2°, Illuminant= D65
#define REF_Y 100.000;
#define REF_Z 108.883;
Color rgb2xyz(int R,int G,int B){
float r = R / 255.0;
float g = G / 255.0;
float b = B / 255.0;
if (r > 0.04045){ r = pow((r + 0.055) / 1.055, 2.4); }
else { r = r / 12.92; }
if ( g > 0.04045){ g = pow((g + 0.055) / 1.055, 2.4); }
else { g = g / 12.92; }
if (b > 0.04045){ b = pow((b + 0.055) / 1.055, 2.4); }
else { b = b / 12.92; }
r = r * 100;
g = g * 100;
b = b * 100;
//Observer. = 2°, Illuminant = D65
Color xyz;
xyz.X = r * 0.4124 + g * 0.3576 + b * 0.1805;
xyz.Y = r * 0.2126 + g * 0.7152 + b * 0.0722;
xyz.Z = r * 0.0193 + g * 0.1192 + b * 0.9505;
return xyz;
}
Color xyz2lab(float X,float Y, float Z){
float x = X / REF_X;
float y = Y / REF_X;
float z = Z / REF_X;
if ( x > 0.008856 ) { x = pow( x , .3333333333f ); }
else { x = ( 7.787 * x ) + ( 16/116.0 ); }
if ( y > 0.008856 ) { y = pow( y , .3333333333f ); }
else { y = ( 7.787 * y ) + ( 16/116.0 ); }
if ( z > 0.008856 ) { z = pow( z , .3333333333f ); }
else { z = ( 7.787 * z ) + ( 16/116.0 ); }
Color lab;
lab.L = ( 116 * y ) - 16;
lab.a = 500 * ( x - y );
lab.b = 200 * ( y - z );
return lab;
}
Color lab2xyz(float l, float a, float b){
float y = (l + 16) / 116;
float x = a / 500 + y;
float z = y - b / 200;
if ( pow( y , 3 ) > 0.008856 ) { y = pow( y , 3 ); }
else { y = ( y - 16 / 116 ) / 7.787; }
if ( pow( x , 3 ) > 0.008856 ) { x = pow( x , 3 ); }
else { x = ( x - 16 / 116 ) / 7.787; }
if ( pow( z , 3 ) > 0.008856 ) { z = pow( z , 3 ); }
else { z = ( z - 16 / 116 ) / 7.787; }
Color xyz;
xyz.X = x * REF_X;
xyz.Y = y * REF_Y;
xyz.Z = z * REF_Z;
return xyz;
}
Color xyz2rgb(float X,float Y,float Z){
//X from 0 to 95.047 (Observer = 2°, Illuminant = D65)
//Y from 0 to 100.000
//Z from 0 to 108.883
X = ofClamp(X, 0, 95.047);
float x = X * .01;
float y = Y * .01;
float z = Z * .01;
float r = x * 3.2406 + y * -1.5372 + z * -0.4986;
float g = x * -0.9689 + y * 1.8758 + z * 0.0415;
float b = x * 0.0557 + y * -0.2040 + z * 1.0570;
if ( r > 0.0031308 ) { r = 1.055 * pow( r , ( 1 / 2.4f ) ) - 0.055; }
else { r = 12.92 * r; }
if ( g > 0.0031308 ) { g = 1.055 * pow( g , ( 1 / 2.4f ) ) - 0.055; }
else { g = 12.92 * g; }
if ( b > 0.0031308 ) { b = 1.055 * pow( b , ( 1 / 2.4f ) ) - 0.055; }
else { b = 12.92 * b; }
Color rgb;
rgb.R = round( r * 255 );
rgb.G = round( g * 255 );
rgb.B = round( b * 255 );
return rgb;
}
Color rgb2lab(int R,int G,int B){
Color xyz = rgb2xyz(R, G, B);
return xyz2lab(xyz.X, xyz.Y, xyz.Z);
}
Color lab2rgb(int L,int a,int b){
Color xyz = lab2xyz(L, a, b);
return xyz2rgb(xyz.X, xyz.Y, xyz.Z);
}
Color getAverage(ofImage img){
Color avg;
avg.L = avg.a = avg.b = 0;
int total = img.width * img.height;
for(int y = 0 ; y < img.height; y++){
for(int x = 0 ; x < img.width; x++){
ofColor c = img.getColor(x, y);
Color lab = rgb2lab(c.r,c.g,c.b);
avg.L += lab.L;
avg.a += lab.a;
avg.b += lab.b;
}
}
avg.L /= total;
avg.a /= total;
avg.b /= total;
return avg;
}
ofImage images[6];
Color averages[6];
ofColor averagesRGB[6];
ofImage colorPicker;
ofColor searchClr;
int closestId = -1;
//--------------------------------------------------------------
void testApp::setup(){
colorPicker.loadImage("colormap.gif");
images[0].loadImage("red.jpg");
images[1].loadImage("green.jpg");
images[2].loadImage("blue.jpg");
images[3].loadImage("cyan.jpg");
images[4].loadImage("magenta.jpg");
images[5].loadImage("yellow.jpg");
for(int i = 0 ; i < 6; i++){
averages[i] = getAverage(images[i]);
Color avgRGB = lab2rgb(averages[i].L, averages[i].a, averages[i].b);
averagesRGB[i] = ofColor(avgRGB.R,avgRGB.G,avgRGB.B);
}
}
//--------------------------------------------------------------
void testApp::update(){
//pick a colour
searchClr = colorPicker.getColor(mouseX,mouseY-500);
//find closest - might want to that on an event
Color searchLab = rgb2lab(searchClr.r, searchClr.g, searchClr.b);
float minDist = 10000000;
for(int i = 0 ; i < 6; i++){
Color Lab = averages[i];
float dL = Lab.L - searchLab.L;
float da = Lab.a - searchLab.a;
float db = Lab.b - searchLab.b;
float dist = sqrt(dL*dL + da*da + db*db);
if(dist < minDist){
minDist = dist;
closestId = i;
}
}
}
//--------------------------------------------------------------
void testApp::draw(){
for(int i = 0 ; i < 6; i++){
//indexed image
images[i].draw(images[i].width * i, 0);
//average colour
ofPushStyle();
ofSetColor(averagesRGB[i]);
ofRect(images[i].width * i, images[i].height, images[i].width, images[i].width);
ofPopStyle();
}
ofPushStyle();
ofSetColor(searchClr);
ofRect(200,500,200,200);
ofPopStyle();
colorPicker.draw(0,500);
if(closestId >= 0){
images[closestId].draw(400, 500);
}
}
//--------------------------------------------------------------
void testApp::keyPressed(int key){
}
//--------------------------------------------------------------
void testApp::keyReleased(int key){
}
//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y){
}
//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){
}
//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
}
//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){
}
//--------------------------------------------------------------
void testApp::windowResized(int w, int h){
}
//--------------------------------------------------------------
void testApp::gotMessage(ofMessage msg){
}
//--------------------------------------------------------------
void testApp::dragEvent(ofDragInfo dragInfo){
}
The coding style isn't brilliant but it's just to illustrate the idea. Of course you would need to load the images from the url first and index the average colour in Lab* for each in a database (vector at runtime or otherwise).
The above code is also available as an Xcode project