How to tint an ofImage

Hello, I’m trying the following code

for (int x = 0; x < photo->getWidth(); ++x)
{
	for (int y = 0; y < photo->getHeight(); ++y)
	{
		ofColor c = photo->getColor(x, y);
		float rt = ofClamp(bend - 1.0f, 0.0f, 1.0f);
		int r = ofLerp(c.r, 255, rt);
		ofColor n(r, c.g, c.b);
		changedPhoto->setColor(x, y, n);
	}
}

so that when the “bend” value goes up, all pixels get redder than they are in the original image. I’m getting the error in the console while the app window freezes up:

[warning] ofPixels: setting color not supported yet for UNKOWN format

So what’s the correct way to do this?

Hi,
Have you allocated changedPhoto?
If not, you can copy it, like this:
ofImage changedPhoto = originalPhoto; // This copies the allocted memory from the loaded original.
Or otherwise you can call:
changedPhoto.allocate(w, h, OF_IMAGE_COLOR); // w and h are the dimensions in pixels

If this isn’t the issue, you can perhaps share the complete code.

OK, that error is gone now, but the program becomes very slow and laggy and the tint also doesn’t work. Commenting out the double for-loop and replacing it with changedPhoto->setColor(ofColor::red); removes the lag, but there’s still no color change.

Nb. I’ve tried the below code with changedPhoto = make_shared<ofImage>(*photo); instead of changedPhoto->allocate(photo->getWidth(), photo->getHeight(), OF_IMAGE_COLOR_ALPHA); which draws a photo, but the tint still doesn’t work.

EDIT: @Jildert The script class is a bit long, so here’s a short example script that has the same problem:

#include "ofMain.h"
#include "ofxGui.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();
		
		std::shared_ptr<ofImage> photo = nullptr;
		std::shared_ptr<ofImage> changedPhoto = nullptr;
		ofxPanel gui;
		ofParameter<float> bend = { "bend", 1.0, 0.0, 2.0 };
};
void ofApp::setup(){
	photo = make_shared<ofImage>();
	photo->load("D:\\img.PNG");
	changedPhoto = make_shared<ofImage>(*photo);
	//changedPhoto->allocate(photo->getWidth(), photo->getHeight(), OF_IMAGE_COLOR_ALPHA);
	gui.setup();
	gui.add(bend);
}
void ofApp::draw(){
	for (int x = 0; x < photo->getWidth(); ++x)
	{
		for (int y = 0; y < photo->getHeight(); ++y)
		{
			ofColor c = photo->getColor(x, y);
			float rt = ofClamp(bend - 1.0f, 0.0f, 1.0f);
			int r = ofLerp(c.r, 255, rt);
			ofColor n(r, c.g, c.b);
			changedPhoto->setColor(x, y, n);
		}
	}
	//changedPhoto->setColor(ofColor::red);
	changedPhoto->draw(0,0,changedPhoto->getWidth(),changedPhoto->getHeight());
	gui.draw();
}

Hi,
Thanks for the code (and the updated minimal version).
First clue:
Call changedPhoto->update(); after the for-loops, this draws the original image.
I’ll update this reply soon.

Oh, thanks. I didn’t know about ofImage::update(). The tint works now, but it’s still super laggy. I get that the double for loop is huge, so what’s a better way to do this?

Yes, it’s also slow over here, around 6 fps.
I think the answer will be: use a shader (and use GPU instead of CPU).
But there’s a topic on the forum which answers this question as well, with the same conclusion I think. But with some more info than I can give, I’m searching for it :slight_smile:

1 Like

Using this makes it a bit faster (18 fps instead of 6):

for(int i=0; i<photo->getPixels().size(); i+=3){ // Only first channel
        unsigned char originalValue = photo->getPixels().getData()[i];
        originalValue = ofClamp(originalValue + 100, 0, 255); // Random + 100, to demonstrate red channel increase
        changedPhoto->getPixels().getData()[i] = originalValue; // Well... Not original anymore. 
    }

But this will be too slow. So, yes, I think a shader will give you good performance.
You can try examples/shader/05_alphaMasking and ignore the alphaMasking-part.
The example shows how to read a texture and how to write a color based on this (in the example it’s based on 2 textures (the original and the mask)).

Oh, and in your code I think you’re not actually using 2 ofImages, since you’re creating a second pointer, pointing to the original ofImage. But I’m not sure, I don’t use shared_ptr.
The single image-object is not a problem, but just mentioning it for completeness.

Yeah, I thought a shader might be the answer… Thank you! I’ll look into that example.

make_shared() makes a new object and returns a pointer to it. If they were both pointing to the same image, then c.r would keep returning higher values until you got stuck at 255 and unable to unredden the screen, which is not the case here. (This is why I’m using two images)

1 Like

Ah yes, thanks for the info. Makes sense now.

Hi. Not sure if this helps. You could also tint an image through standard openGL globally on all pixels without shaders, i.e., like in watercolor painting, painting airframes, multiple blendmodes, layers, its way faster than per pixel changes, also with its limitations


ofImage img;
void ofApp::setup(){
    img.load("img.jpg");
    img.resize(320,240);
}

void ofApp::draw(){
    ofEnableAlphaBlending();
    ofBlendMode(OF_BLENDMODE_MULTIPLY);
    ofSetColor(255);
    img.draw(0,0,320,240);
    ofSetColor(ofMap(ofGetMouseX(),0,ofGetWidth(),0,255),255,0, 200);
    ofRect(0,0,320,240);
}

Btw, thinking on this I tried the blending example, and is currently crashing on tiff’s somewhere, latest manjaro linux, jpgs seem to be loading ok.

gdb bin/blendingExample 
GNU gdb (GDB) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bin/blendingExample...
(gdb) r
Starting program: /of0.11.2/examples/graphics/blendingExample/bin/blendingExample 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
[New Thread 0x7fffea97b640 (LWP 11024)]
[New Thread 0x7fffea17a640 (LWP 11025)]
[New Thread 0x7fffe9979640 (LWP 11026)]
[New Thread 0x7fffe9178640 (LWP 11027)]
[New Thread 0x7fffe8957640 (LWP 11028)]
double free or corruption (out)

Thread 1 "blendingExample" received signal SIGABRT, Aborted.
0x00007ffff6d5436c in ?? () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00007ffff6d5436c in  () at /usr/lib/libc.so.6
#1  0x00007ffff6d04838 in raise () at /usr/lib/libc.so.6
#2  0x00007ffff6cee535 in abort () at /usr/lib/libc.so.6
#3  0x00007ffff6d4845e in  () at /usr/lib/libc.so.6
#4  0x00007ffff6d5e0cc in  () at /usr/lib/libc.so.6
#5  0x00007ffff6d60250 in  () at /usr/lib/libc.so.6
#6  0x00007ffff6d628f3 in free () at /usr/lib/libc.so.6
#7  0x00007ffff5a58851 in TIFFFreeDirectory () at /usr/lib/libtiff.so.5
#8  0x00007ffff5a61f25 in TIFFReadDirectory () at /usr/lib/libtiff.so.5
#9  0x00007ffff5a637e2 in TIFFSetDirectory () at /usr/lib/libtiff.so.5
#10 0x00007ffff72b445a in  () at /usr/lib/libfreeimage.so.3
#11 0x00007ffff7284bb2 in FreeImage_LoadFromHandle ()
    at /usr/lib/libfreeimage.so.3
#12 0x00007ffff7284cdf in FreeImage_Load () at /usr/lib/libfreeimage.so.3
#13 0x000055555563c07c in bool loadImage<unsigned char>(ofPixels_<unsigned char>&, boost::filesystem::path const&, ofImageLoadSettings const&) ()
#14 0x0000555555640963 in ofImage_<unsigned char>::load(boost::filesystem::path const&, ofImageLoadSettings const&) ()
--Type <RET> for more, q to quit, c to continue without paging--
#15 0x00005555555cfab0 in ofApp::setup() ()
#16 0x00005555556a95a3 in std::_Function_handler<bool (void const*, ofEventArgs&), ofEvent<ofEventArgs, std::recursive_mutex>::make_function<ofBaseApp>(ofBaseApp*, void (ofBaseApp::*)(ofEventArgs&), int)::{lambda(void const*, ofEventArgs&)#1}>::_M_invoke(std::_Any_data const&, void const*&&, ofEventArgs&) ()
#17 0x0000555555651682 in ofEvent<ofEventArgs, std::recursive_mutex>::notify(ofEventArgs&) ()
#18 0x00005555556a94d6 in ofMainLoop::run(std::shared_ptr<ofBaseApp>&&) ()
#19 0x00005555555d5361 in ofRunApp(ofBaseApp*) ()
#20 0x00005555555b76b6 in main ()
(gdb) 


1 Like

Hey, thanks for the idea. For me it works better with additive mode:

I need to learn more about shaders at some point though, but I’ll put it off for now. xD

Hello @s_e_p ,

Maybe you can use a faster (than the top of the topic options) shader / add-on like this one:

(or GitHub - Akira-Hayasaka/ofxPSBlend: openFrameworks Photoshop blending math with GLSL.)

This add-on is kind of a bundle (maybe you want some simpler single class).
It has this module:
ThreeTones.
You can easy colorize with some range tweaking too.

PS:
BTW, I used this add-on to build another one, where you can look some screenshots using this ranged color tint and other common image proccessing:

1 Like