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.
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.
@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.
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;
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;
};
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.
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.
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:
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.