Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added example-weights/bin/data/0.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example-weights/bin/data/1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example-weights/bin/data/2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
195 changes: 195 additions & 0 deletions example-weights/src/ColorConverter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#pragma once
#include "ofMain.h"

/*

Converting RGB image to LAB in order to get 'Chroma' or colorfullness of a color

*/

class ColorConverter {
public:
static ofVec3f rgbToXyz( const ofFloatColor &rgb ) {
float r = rgb.r;
float g = rgb.g;
float b = rgb.b;

if ( r > 0.04045f ) {
r = pow( ( r + 0.055f ) / 1.055f, 2.4f );
} else {
r = r / 12.92f;
}

if ( g > 0.04045f ) {
g = pow( ( g + 0.055f ) / 1.055f, 2.4f );
}
else {
g = g / 12.92f;
}

if ( b > 0.04045f ) {
b = pow( ( b + 0.055f ) / 1.055f, 2.4f );
}
else {
b = b / 12.92f;
}

r = r * 100.0f;
g = g * 100.0f;
b = b * 100.0f;

// Observer = 2°, Illuminant = D65
return ofVec3f( r * 0.4124f + g * 0.3576f + b * 0.1805f,
r * 0.2126f + g * 0.7152f + b * 0.0722f,
r * 0.0193f + g * 0.1192f + b * 0.9505f );
}

static ofFloatColor xyzToRgb( const ofVec3f &xyz ) {
float x = xyz.x / 100.0f; // X from 0 to 95.047 (Observer = 2°, Illuminant = D65)
float y = xyz.y / 100.0f; // X from 0 to 100.000
float z = xyz.z / 100.0f; // X from 0 to 108.883

float r = x * 3.2406f + y * -1.5372f + z * -0.4986f;
float g = x * -0.9689f + y * 1.8758f + z * 0.0415f;
float b = x * 0.0557f + y * -0.2040f + z * 1.0570f;

if ( r > 0.0031308f ) {
r = 1.055f * pow( r, ( 1.0f / 2.4f ) ) - 0.055f;
}
else {
r = 12.92f * r;
}

if ( g > 0.0031308f ) {
g = 1.055f * pow( g, ( 1.0f / 2.4f ) ) - 0.055f;
}
else {
g = 12.92f * g;
}

if ( b > 0.0031308f ) {
b = 1.055f * pow( b, ( 1.0f / 2.4f ) ) - 0.055f;
}
else {
b = 12.92f * b;
}

return ofColor( r, g, b );
}

static ofVec3f xyzToLab( const ofVec3f &xyz ) {
// Observer = 2°, Illuminant = D65
const float refX = 95.047f;
const float refY = 100.0f;
const float refZ = 108.883f;

float x = xyz.x / refX;
float y = xyz.y / refY;
float z = xyz.z / refZ;

if ( x > 0.008856f ) {
x = pow( x, 1.0f / 3.0f );
}
else {
x = ( 7.787f * x ) + ( 16.0f / 116.0f );
}

if ( y > 0.008856f ) {
y = pow( y, 1.0f / 3.0f );
}
else {
y = ( 7.787f * y ) + ( 16.0f / 116.0f );
}

if ( z > 0.008856f ) {
z = pow( z, 1.0f / 3.0f );
}
else {
z = ( 7.787f * z ) + ( 16.0f / 116.0f );
}

return ofVec3f( ( 116.0f * y ) - 16.0f,
500.0f * ( x - y ),
200.0f * ( y - z ) );
}

static ofVec3f labToXyz( const ofVec3f &lab ) {
float y = ( lab.x + 16.0f ) / 116.0f;
float x = lab.y / 500.0f + y;
float z = y - lab.z / 200.0f;

float x3 = pow( x, 3.0f );
float y3 = pow( y, 3.0f );
float z3 = pow( z, 3.0f );

if ( x3 > 0.008856f ) {
x = x3;
}
else {
x = ( x - 16.0f / 116.0f ) / 7.787f;
}

if ( y3 > 0.008856f ) {
y = y3;
}
else {
y = ( y - 16.0f / 116.0f ) / 7.787f;
}

if ( z3 > 0.008856f ) {
z = z3;
}
else {
z = ( z - 16.0f / 116.0f ) / 7.787f;
}


// Observer = 2°, Illuminant = D65
const float refX = 95.047f;
const float refY = 100.0f;
const float refZ = 108.883f;
return ofVec3f( x * refX, y * refY, z * refZ );
}

static ofVec3f labToLch( const ofVec3f &lab ) {
return ofVec3f( lab.x,
sqrt( lab.y * lab.y + lab.z * lab.z ),
atan2( lab.z, lab.y ) );
}

static ofVec3f lchToLab( const ofVec3f &lch ) {
return ofVec3f( lch.x,
lch.y * cos( lch.z ),
lch.y * sin( lch.z ) );
}

static ofVec3f rgbToLab( const ofFloatColor &rgb ) {
return xyzToLab( rgbToXyz( rgb ) );
}

static ofFloatColor labToRgb( const ofVec3f &lab ) {
return xyzToRgb( labToXyz( lab ) );
}

static float calcSaturation( const ofVec3f &lab ) {
float C2 = lab.y * lab.y + lab.z * lab.z;
float C = sqrt( C2 );
float S = C / sqrt( C2 + lab.x * lab.x );
return S;
}

static float calcChroma( const ofVec3f &lab ) {
const float maxChromaInv = 1.0f / sqrt( 128.0f * 128.0f + 128.0f * 128.0f );
float C2 = lab.y * lab.y + lab.z * lab.z;
float C = sqrt( C2 );
float CH = C * maxChromaInv;
return CH;
}

static float distLab( const ofVec3f c1, const ofVec3f c2){
float dL = c1.x - c2.x;
float da = c1.y - c2.y;
float db = c1.z - c2.z;
return sqrt(dL*dL + da*da + db*db);
}
};
11 changes: 11 additions & 0 deletions example-weights/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "ofMain.h"
#include "ofApp.h"
#include "ofAppGlutWindow.h"

int main( ){

ofAppGlutWindow window;
ofSetupOpenGL(&window, 1024,500, OF_WINDOW);
ofRunApp( new ofApp());

}
67 changes: 67 additions & 0 deletions example-weights/src/ofApp.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "ofApp.h"
#include "ColorConverter.h"

void ofApp::setup(){
quantizeImage(ofToString(index) + ".jpg", 10);
}

void ofApp::update(){}

void ofApp::draw(){
ofBackground(100,100,100);

ofPushMatrix();
ofTranslate(50,50);
ofSetColor(255);
image.draw(0, 0, image.getWidth()/5, image.getHeight()/5);

ofPushMatrix();
ofTranslate(0, image.getHeight()/5);
ofDrawBitmapString("hit space to change image", 0, 30);
ofSetColor(255,100);
ofDrawBitmapString("colors are weighted based on their areas, their order is based on their chroma values", 0, 50);

ofTranslate(image.getWidth()/5 + 20,0);

for(int i=0; i<colorQuantizer.getNumColors(); i++) {
ofSetColor(0,50);
ofDrawRectangle(i*60, 0, 50, -image.getHeight()/5);
ofSetColor(sortedColors[i].color);
ofDrawRectangle(i*60, 0, 50, ofMap(sortedColors[i].weight, 0, 1, 0, -image.getHeight()/5));
ofSetColor(255);
ofDrawBitmapString(ofToString(int(sortedColors[i].weight * 100)) + "%", i * 60,30);
}

ofPopMatrix();
ofPopMatrix();
}

void ofApp::quantizeImage(string imgName, int numColors) {
image.load(imgName);
ofImage imageCopy;
imageCopy.load(imgName);
imageCopy.resize(imageCopy.getWidth()/2, imageCopy.getHeight()/2);

colorQuantizer.setNumColors(numColors);
colorQuantizer.quantize(imageCopy.getPixels());

sortedColors.clear();;
sortedColors.resize(colorQuantizer.getNumColors());
for(int i = 0; i < colorQuantizer.getNumColors(); i++) {
ofFloatColor fc = ofFloatColor(colorQuantizer.getColors()[i].r/255.0, colorQuantizer.getColors()[i].g/255.0, colorQuantizer.getColors()[i].b/255.0);
ofVec3f labCol = ColorConverter::rgbToLab(fc);

sortedColors[i].distance = ColorConverter::calcChroma(labCol);
sortedColors[i].color = colorQuantizer.getColors()[i];
sortedColors[i].weight = colorQuantizer.getColorWeights()[i];
}

std::sort(sortedColors.begin(), sortedColors.end(), by_distance());
}

void ofApp::keyPressed(int key){
index++;
if(index>2) index = 0;
quantizeImage(ofToString(index) + ".jpg", 10);
}
void ofApp::keyReleased(int key){}
35 changes: 35 additions & 0 deletions example-weights/src/ofApp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once
#include "ofMain.h"
#include "ofxOpenCv.h"
#include "ofxColorQuantizer.h"

class ofApp : public ofBaseApp{
public:
void setup();
void update();
void draw();

void keyPressed(int key);
void keyReleased(int key);

ofxColorQuantizer colorQuantizer;
void quantizeImage(string imageName, int numColors);

ofImage image;
int index = 0;

struct weightedColor {
ofColor color;
float weight;
float distance;
};

vector< weightedColor > sortedColors;

struct by_distance {
bool operator()(weightedColor const &a, weightedColor const &b) {
return a.distance > b.distance;
}
};
};

21 changes: 20 additions & 1 deletion src/ofxColorQuantizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ vector<ofColor> & ofxColorQuantizer::quantize(ofPixels inputImage){
cv::Mat labels, clusters;
cv::kmeans( colorSamples, colorCount, labels, cv::TermCriteria(), 2, cv::KMEANS_RANDOM_CENTERS, clusters ); //cv::TermCriteria::COUNT, 8, 0

// calculate histogram
histogram.clear();
histogram.resize( colorCount );

for( int i = 0; i < labels.rows; i++ ) {
++histogram[labels.at<int>(i, 0)];
}
float sum = 0;
for(int i=0; i<histogram.size(); i++) {
histogram[i] = histogram[i]/colorSamples.rows;
sum+=histogram[i];
}


// clear our list of colors
colors.clear();

Expand Down Expand Up @@ -73,7 +87,7 @@ void ofxColorQuantizer::draw(ofPoint pos){
while( cIter != colors.end() ){

ofSetColor(*cIter);
ofRect(0, 0, swatchSize, swatchSize);
ofDrawRectangle(0, 0, swatchSize, swatchSize);
ofTranslate(swatchSize, 0, 0);
cIter++;
}
Expand All @@ -100,3 +114,8 @@ int ofxColorQuantizer::getNumColors(){

return numColors;
}

vector< float > ofxColorQuantizer::getColorWeights() {
return histogram;
}

4 changes: 3 additions & 1 deletion src/ofxColorQuantizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ class ofxColorQuantizer {
int getNumColors();

vector<ofColor> & getColors();
vector<float> getColorWeights();

protected:

vector<ofColor>colors;
unsigned int numColors;
};
vector<float>histogram;
};