Change the hue or saturation of an image


#1

With this code, I want to change the hue of the image and not the background but I’m not sure what I need to change? This is pretty much my first processing project so I’m not of any of the terms or jargon.

Using an arduino and a photoresistor, i’m able to get the background to change but need to change it so the photoresistor changes the hue of the image.

// import the serial library
  import processing.serial.*;

  // create an instance of the serial library
  Serial myPort;

  // create an instance of PImage
  PImage logo;

  // a variable to hold the background color
  int bgcolor = 0;

  void setup() {
    size(1, 1);
    surface.setResizable(true);
    // set the color mode to Hue/Saturation/Brightness
    colorMode(HSB, 255);

    // load the Arduino logo into the PImage instance
    logo = loadImage("http://www.arduino.cc/arduino_logo.png");

    // make the window the same size as the image
    surface.setSize(logo.width, logo.height);

    // print a list of available serial ports to the Processing status window
    println("Available serial ports:");
    printArray(Serial.list());

    // Tell the serial object the information it needs to communicate with the
    // Arduino. Change Serial.list()[0] to the correct port corresponding to
    // your Arduino board.  The last parameter (e.g. 9600) is the speed of the
    // communication.  It has to correspond to the value passed to
    // Serial.begin() in your Arduino sketch.
    myPort = new Serial(this, Serial.list()[1], 9600);

    // If you know the name of the port used by the Arduino board, you can
    // specify it directly like this.
    // port = new Serial(this, "COM1", 9600);
    //myPort = new Serial(this, "/dev/ttyACM0", 9600);
  }

  void draw() {

    // if there is information in the serial port
    if ( myPort.available() > 0) {
      // read the value and store it in a variable
      bgcolor = myPort.read();
      // print the value to the status window
      println(bgcolor);
    }

    // Draw the background. the variable bgcolor contains the Hue, determined by
    // the value from the serial port
    background(bgcolor, 255, 255);

    // draw the Arduino logo
    image(logo, 0, 0);
  }

^ Processing code

void setup() {
  // starting serial communication. Make sure Processing and Arduino have the same serial baud rate.
 Serial.begin(9600);
}

void loop() {
  // Sending to the serial connection the analug inputs introcued by the potentiometer.
  // As only is possible to send values from 0 to 255, devide de AnalogRead value by 4.
 Serial.write(analogRead(A0)/4);
  // After sending the byte, let the ADC stabilize.
 delay(33);
}

^arduino code

I imagine its fairly simple but I really don’t know what I’m doing. Thanks.


#2

You should be able to change the hue of the PImage using the tint() function on it, after calling background().
Just call background() with the color you want. Then tint() with the hue for the image and then draw the image.


#3

Thank you for providing the code. I am not sure if Processing will allow you to set an image of size(1,1) and I am sure you are not trying to do that either. I can suggest implementing the following change:

void settings() {
  logo = loadImage("http://www.arduino.cc/arduino_logo.png");
  size(logo.width, logo.height);
}

void setup() {
  ....   FUNCTIONS call in setup
}

void draw() {
  .....  FUNCTIONS call for draw
}

This will ensure your sketch size matches your image size when the sketch starts running.

To change the hue, I need to understand a bit better what you are trying to do. When you get a value from your arduino, you want to scale all the hue values proportional to the data received from your arduino? I can only think of few ideas of what you could do but I prefer if you tell me what the outcome will look like. In other words, what are the expectations. Do you want to add to the hue value of each pixel or do you want to multiply each hue value by a factor?

In the meantime you can check this example that shows how to change the hue value:

https://processing.org/reference/pixels.html

The next link shows bit shifting, which it is useful when you are doing pixel manipulation. Not sure if you will understand it. If you don’t, I suggest you do a google search about bit shifting as there are very good tutorials out there explaining the concept.

https://processing.org/reference/rightshift.html

Notice that the bit shifting above is done in the RGB color space. This can be done similarly in HSB color space as you will need to replace the red, green and blue by hue, saturation and brightness respectively.

Your draw will look something like below.

Kf

void draw() {

    // if there is information in the serial port
    if ( myPort.available() > 0) {
      image(logo,0,0);
      readInput = myPort.read();
      loadPixels();
    
      for (int i=0;i<width*height;i++{
           color hsbPixel = pixels[i];
           int a = (hsbPixel >> 24) & 0xFF;
           int h = (hsbPixel >> 16) & 0xFF;  
           int s = (hsbPixel >> 8) & 0xFF;  
           int b = hsbPixel & 0xFF;    

           // Now "change" the hue here... you need to define the change yourself as you did not provide this details
          //
          // To be done....

          //Now update the pixels  
          pixels[i]   = color(h,s,b,a);  
      }
      updatePixels();  //Need to be called every time you modify a pixel, which you did above
    
    }// END if (port avail)
}

#4

Actually, processing allows the size of 1… just tried it^^ Thought it would autoset it to 50/default, but apparently not. Though the size of the window per se is still default, just the displayed part is not.


#5

Yes, actually tint should do the trick: https://processing.org/reference/tint_.html only if it manipulates hue the way you want it. If you want to define your own hue change functionality, you will need to do it manually.

Notice that to use tint, you will need to map your arduino input value into the color space range.

When you change your color space from RGB to HSB, you are defining the color range of each component and you do this in setup:

colorMode(HSB, 255);

Now if your arduino is a 10bit value (it provides a value from 0 to 1023) then you need to map this value to the color range of 256 as provided above. You can do that using the map function:

final int ARDUINO_MIN=0;
final int ARDUINO_MAX=1023;
int arduinoInput = myPort.read();
color inColor = map(arduinoInput, ARDUNINO_MIN, ARDUINO_MAX,0,255);
tint(inColor, 255,255);

This is sort of the concept. However it is important to test the code and modify it to your needs.

Kf


#6

I’m sorry if I’m not being technical enough with my wording as this is my first time delving in to arudino and processing. I have no previous experience of coding or programming so this is all pretty new to me.

What I wanted to achieve is that if you turn a potentiometer, the hue of an image would change accordingly in real time. As if you were sliding the hue bar on photoshop and seeing the hue change on your image.

I have managed to change the background colour using this but I’m not sure where to start with a actual image.

I use a png image that’s saved on my computer if that chnages anything.

Thanks for the help so far


#7

I have this atm but it’s obviously not doing anything. Not exactly what I’m doing wrong and why it’s wrong.

I know my code is very wrong so please don’t judge :frowning:

Arduino code:

void setup() {
  // starting serial communication. Make sure Processing and Arduino have the same serial baud rate.
 Serial.begin(9600);
}

void loop() {
  // Sending to the serial connection the analug inputs introcued by the potentiometer.
  // As only is possible to send values from 0 to 255, devide de AnalogRead value by 4.
 Serial.write(analogRead(A0)/4);
  // After sending the byte, let the ADC stabilize.
 delay(33);
 
final int ARDUINO_MIN=0;
final int ARDUINO_MAX=1023;
int arduinoInput = myPort.read();
color inColor = map(arduinoInput, ARDUNINO_MIN, ARDUINO_MAX,0,255);
tint(inColor, 255,255);
}

Processing code:

import processing.serial.*;
Serial myPort;

PImage img;

int tint=0;

void setup() {
  
  colorMode(HSB,255);
   
  // load the image file from the "data" folder
  img = loadImage("cat.png");

  // set the window to be the same dimensions as the image
  size(400, 400);
  
  
 println("Available serial ports:");
 printArray(Serial.list());
 //Telling Processing information about the serial connection. The parameters are: which application will be speaking to, which serial port will be communicating(depending on the previous result), and at what speed.
 myPort=new Serial(this,Serial.list()[1],9600);
}

void draw() {
  
 if(myPort.available()>0){
 tint=myPort.read();
 println(tint);
  
  // set the image's tint
  tint(200, 0, 200);  // bluish tint

  // display the image so that its upper-left corner
  // is on the upper-left corner of the screen
  image(img, 0, 0);
}
}

#8

You should always have size() as the first statement inside setup(). As far as i can see, that should be the reason why it’s not working.


#9

@Lexyth One can place size() anywhere in setup() and it will still work. It is recommended to be the first line of setup but it is not mandatory. It is just a common practice as it makes the code easier to read. Processing can detect size and it will take it into account when running the application. I believe what happens is that Processing move size to settings and settings gets executed first before setup.

@ultratape There is a easier way to tackle your challenge. I suggest you become familiar with pixel manipulation - changing hue to your likes. But first, it is important to define this change. For this, you can work on a Processing sketch first. You do not need to use the arduino code for the moment. Instead, you can use the mouse pointer’s position to drive the change of color. For instance, this demo below adjust the hue. If your mouse pointer is in the left edge, hue is set to zero. If it is in the extreme right, hue is the its maximum as defined by its range. If the mouse pointer is between left or right, the hue would adjust accordingly and proportionally thanks to the map() function:

//===========================================================================
// GLOBAL VARIABLES:
PImage img;


//===========================================================================
// PROCESSING DEFAULT FUNCTIONS:


void setup() {
  size(640, 360);
  colorMode(HSB, 255, 255, 255);
  img = loadImage("https://imageshack.com/a/img921/9373/4DnUoA.jpg");  
  img.resize(width, height);  //Match the size of th sketch
}

void draw() {
  if (img!=null) {
    image(img, 0, 0);

    float newVal = map(mouseX, 0, width, 255, 0);

    loadPixels();
    for (int x = 0; x < img.width; x++) {
      for (int y = 0; y < img.height; y++ ) {

        // Calculate the 1D location from a 2D grid
        int loc = x + y*img.width;

        // Get the HSB values from image: HERE I am reading all the values (but I do nothing with them aka it is really not needed)
        float  h, b, s;    
        h = hue (img.pixels[loc]);
        b = brightness (img.pixels[loc]);
        s = saturation (img.pixels[loc]);

        
        h = newVal;  //HERE you can modify this code to manage the HUE value
        //s += adjustbrightness;
        //b += adjustbrightness;

        color c = color(h, s, b);
        pixels[loc] = c;
      }
    }
    updatePixels();
  }
}

You can also check the AlphaMask and Transparency examples here as it shows other approaches. You also want to check the filters as you might ind them useful for your case.

After you define this hue function, then you can integrated with your arduino code. For this, you need to set newValue to the value read from the arduino. By the way, I believe you had some processing code in your arduino code above. Ensure you don’t miss the code as one is C and the other is java.

Kf


#10

Still, one should have it first, either because of convention or because of code reasons :sweat_smile: and in a recent Post i saw something about how size works… But that doesn‘t have much to do with this question anymore, so i‘ll just leave it at that.