Blue part of pixel not updating

I am trying to do pixel point processes where I iteratively add some number to every component of every pixel of an image. I can update the red and green components of every pixel but for some reason this fails for the blue component. Here is my ofApp.cpp:

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
	img.load("lineflower.png");
	W = ofGetWidth();
	H = ofGetHeight();
	img.resize(W, H);
	src = img.getPixels().getData();
}

//--------------------------------------------------------------
void ofApp::update(){
	for (int x = 0; x < W; x++) {
		for (int y = 0; y < H; y++) {
			int idx = 3 * (y * W + x);
			src[idx+2] += 4;
		}
	}
	img.update();
}

//--------------------------------------------------------------
void ofApp::draw(){
	img.draw(0, 0, W, H);
}

When I replace idx + 2 with idx or idx + 1 I get a constantly changing visual, but when I use idx + 2 I get a still image. It seems like for some reason it cannot access the blue components of pixels?

hello! not in a position to run the code but I suspect the PNG makes a 4-channel img and you are thrown off by your expectations of colours. what happens if you change:

    int idx = 3 * (y * W + x);

to:

    int idx = 4 * (y * W + x);

that being said, 2 things to note: a) accessing pixels like this in memory is inefficient. if you you’re looking to do pixel manipulation, jump in the fragment shaders in ofBook - Introducing Shaders ; and b) accessing the raw bytes with getData() behind an ofImage can produce unexpected results, as the memory alignement might not be what you expect — the various calls to ofPixels (getColor(), getLines()) with know this, and correctly access the data.

1 Like

Hi, thank you! Tried multiplying by 4 but got a read access violation. I think it’s an RGB image and I get the same issue with JPG as well. I also changed my code to use getColor() and setColor() but now none of the colors are updating.

void ofApp::update(){
	for (int x = 0; x < W; x++) {
		for (int y = 0; y < H; y++) {
			ofColor c = src.getColor(x, y);
			ofColor newcol(c.r+1, c.g, c.b);
			src.setColor(x, y, newcol);
		}
	}
	img.update();
}

where src = img.getPixels(). I guess I don’t understand why my original example worked for updating red and green but not blue?
I will also try Shaders, as you suggest. Could I also try using img.getTexture()?

Hey I love what @burton said about using an ofShader for this task. Shaders run on a gpu and are very, very fast at image processing. They are pretty easy to use but there is a learning curve. But they’re an amazing tool and I feel they are very much worth the time and effort to learn how to use them.

ofPixels::getNumChannels() will return the number of channels in the ofPixels so you don’t have to assume or guess.

The following uses a reference to the ofPixels in the image. A reference allows access to the actual pixels in the image (and not a copy of them). And you don’t need a pointer (like what is returned by ofPixels::getData()). I like to think of a reference as a pointer that you don’t have to dereference to use.

void ofApp::update(){
    // get a reference to the ofPixels in the image
    ofPixels& pixels = img.getPixels();
    // get the number of channels
    size_t numChannels = pixels.getNumChannels();
    // iterate with a single loop using an increment of numChannels
    for(size_t i{0}; i < pixels.size(); i += numChannels){
        // increment the blue channel
        pixels[i+2] += 4;
    }
    
    image.update();
}

Edit: I also ran a version of your original code and it worked fine on a Mac. I’m not sure what type src is; I used an unsigned char*. It might be worth trying some different image files just to eliminate the image file as a possibility.

I do remember a forum thread or two in the past where it seemed like the compiler wasn’t picking the right way to recreate the ofPixels for an image when it changed (like with .resize()).

There are quite a few forum threads with the design pattern in the above code. So just post back if it doesn’t work for some reason.

2 Likes

@Yonkatron can you share the image? and also yes as @TimChi says there is a learning curve to the shaders but if you’re interested it’s worth it (and also an open-ended endeavour as it’s not specifically tied to openFrameworks). I also recommend GitHub - andreasmuller/ofxAutoReloadedShader as you can edit the shaders in an external text file and as you save the text file in whatever editor, the shader is re-uploaded/re-compiled, live-coding style, without restarting the OF app. it’s pretty fast to try things and mistakes are less dispiriting.

(NB: I personnally find fragment shaders intuitive as they map a 2D rectangle of pixels. vertex, geometry (and compute) shader are more abstract — but you don’t need to know about them to write fragment shaders).

getTexture() is useful to get a handle on a given texture floating in the GPU’s VRAM, but in the case of your usage it’s not required as you will simply draw() the image “into” the shader to get processed, and all the GL texture stuff will be handled automatically down to the screen. then your next friend will be ofFbo.

1 Like

In order for this to work, src should be the type ofPixels& (the reference). It can be declared in ofApp.h like this:

ofPixels& src;

If ‘src’ is an ofPixels type (not a reference), then the reference returned by .getPixels() becomes a new copy of the ofPixels in the image. So any changes from calling .setColor() are made to the new copy and not the one in the image, and these changes never make their way back to into the image.

Just to make it all a bit more complicated, you can also call ofImage::setFromPixels() to set some new pixels for an image. So the following should also work:

ofPixels src;
src = image.getPixels(); // a copy of the pixels in the image
// make some changes to the copy, then
image.setFromPixels(src);
image.update()

I think this line in your original code should check if you are not overflowing.
if the value is already 255 or near and you sum 4 to it
src[idx+2] += 4;

1 Like

Thanks everyone! I tried @TimChi’s idea and it didn’t increment the blue. When I try with i+1 or i it does increment the red/green. And getNumChannels() does return 3. I also tried your second suggestion using setFromPixels(src) and that got the same results.
@dimitre I added a % 256 but that didn’t help. Even if the % wasn’t there it should still increment until it reaches 256.
This happens for a lot of different images, and even when I don’t resize the image. I even started restarting my computer.
I suppose using Shaders is a good long term solution, but my applications have been working quickly enough with what I’ve been doing and it just doesn’t make sense to me why this would work for green and red but not for blue.
Thanks again, really appreciate the help.

Yeah this behavior is very very strange. Maybe post some details about hardware, os, OF version, etc, if you have a chance. One thing you could do too is to make some new projects with a very very basic test in each one. Maybe starting with ofImage and then incorporating ofPixels. A first one might be something like this:
ofApp.h:

#pragma once
#include "ofMain.h"
class ofApp : public ofBaseApp{
	public:
		void setup();
		void update();
		void draw();
    ofImage image;
};

ofApp.cpp: quick study of ofImage::setColor()

#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
    image.allocate(ofGetWidth(), ofGetHeight(), OF_IMAGE_COLOR);
    for(size_t y{0}; y < image.getHeight(); ++y){
        for(size_t x{0}; x < image.getWidth(); ++x){
            float blue = ofRandom(255);
            image.setColor(x, y, ofColor(0, 0, blue));
        }
    }
    image.update();
}
//--------------------------------------------------------------
void ofApp::update(){

}

//--------------------------------------------------------------
void ofApp::draw(){
    image.draw(0.f, 0.f);
}

Sure. When I run ofGetVersionInfo() I get 0.11.2-master. I am running Windows 11 on a Dell XPS 13. Not sure if it’s relevant, but I’m using Visual Studio 2019, so whenever I make or update a project I have to retarget the solution.

Interesting. Tried this example and it doesn’t work. Does work for red and green though.

Ugh something is definitely not right. I don’t know windows and Visual Studio at all so I won’t be much help.

One thing you could do is to try the nightly build, get it set up in Visual Studio 2019 as its own “fresh, new, and separate thing”, and then see how it goes.

Hopefully someone who knows Visual Studio can help with some suggestions; maybe @Theo has some insight.

1 Like

let’s try it the other way around: please run the attached code with the attached png, and validate if what you get looks like this video: blue

ofApp.cpp (874 Octets)
ofApp.h (207 Octets)
test.png.zip (289,0 Ko)

(note: .png is zipped otherwise the forum transcodes it to jpg)

3 Likes

I get the same outputs in the console but I just get a still image. Works for red and green though.

Remember using modulus will zero the blue if it overflows.
it seems it is not the issue there, but if you want to try, maybe you should use ofClamp to clamp values between 0 to 255

hmmm… but how come the blue works here for me (macOS13/OF0.11.2-master) but not there? NB here are what is printed in the console about the PNG I provided:

[notice ] getNumPlanes: 1
[notice ] getNumChannels: 4
[notice ] getBytesStride: 4096
[notice ] getPixelFormat: 4
[notice ] getBitsPerPixel: 32

Yes that is certainly the question. Maybe a Visual Studio issue? Or something to do with hardware/my graphics card? When I display an image in OF, it retains its blue components, and I can even see the blue component of a given pixel in the debugger. But for some reason I can’t do stuff with them.
Update: Tried with the nightly build but no luck.

Have you tried both Debug and Release, for any of the projects (maybe the one from burton)? Frustrating and thanks for trying all of our suggestions so far! I’m glad you tried the nightly too.

Yeah I tried both. Thanks for all of the suggestions! I will try to use Shaders, happy to hear other suggestions if they come up.

1 Like

ok well just out of curiosity, what do you see when you run the examples/graphics /colorsExtendedExample?