Converting RGB to HSB (aka HSV) - java

I am trying to implement a simple RGB to HSB routine, I've closely followed instruction from wikipedia reference article.
The code I have written is:
#include <iostream>
#include <cmath>
struct RGB { float red, green, blue; };
struct HSB { float hue, saturation, brightness; }; // aka HSV
// https://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB
static void HSBToRGB(HSB const & hsb, RGB & rgb )
{
/*
* Given a color with hue H ∈ [0°, 360°), saturation SHSV ∈ [0, 1], and value
* V ∈ [0, 1], we first find chroma:
*/
const float H = hsb.hue;
const float S_HSV = hsb.saturation;
const float V = hsb.brightness;
const float C = V * S_HSV;
/*
* Then we can find a point (R1, G1, B1) along the bottom three faces of the RGB
* cube, with the same hue and chroma as our color (using the intermediate value X
* for the second largest component of this color):
*/
const float H_prime = H / 60.;
const float X = C * ( 1 - std::abs( (int)H_prime % 2 - 1) );
float R1, G1, B1;
if( isnan( H ) )
{
R1 = G1 = B1 = 0;
}
else if( 0 <= H_prime && H_prime < 1 )
{
R1 = C; G1 = X; B1 = 0;
}
else if( 1 <= H_prime && H_prime < 2 )
{
R1 = X; G1 = C; B1 = 0;
}
else if( 2 <= H_prime && H_prime < 3 )
{
R1 = 0; G1 = C; B1 = X;
}
else if( 3 <= H_prime && H_prime < 4 )
{
R1 = 0; G1 = X; B1 = C;
}
else if( 4 <= H_prime && H_prime < 5 )
{
R1 = X; G1 = 0; B1 = C;
}
else if( 5 <= H_prime && H_prime < 6 )
{
R1 = C; G1 = 0; B1 = X;
}
/*
* Finally, we can find R, G, and B by adding the same amount to each component,
* to match value:
*/
const float m = V - C;
rgb.red = R1 + m;
rgb.green = G1 + m;
rgb.blue = B1 + m;
}
int main()
{
HSB const hsb = { 251.1f, 0.887f, 0.918f };
RGB rgb = { 0.255f , 0.104f , 0.918f };
std::cout << "Reference: " << rgb.red << "," << rgb.green << "," << rgb.blue << std::endl;
HSBToRGB(hsb, rgb);
std::cout << "Computed: " << rgb.red << "," << rgb.green << "," << rgb.blue << std::endl;
return 0;
}
When compiled and run on linux debian/jessie amd64 here is what I get:
$ ./ref
Reference: 0.255,0.104,0.918
Computed: 0.103734,0.103734,0.918
Now if you lookup the correct reference values (same article, line 11 in the table) it should have been the same. What I do not understand is the large difference value for Hue (0.255 != 0.103734).
I've been double checking my implementation with what the wikipedia article describe and could not spot any difference.
Using java as reference, I could check that the RGB values should be correct, eg:
import java.awt.Color;
public class HSBToRGBExample {
public static void main(String[] args) {
float hue = 251.1f / 360;
float saturation = 0.887f;
float brightness = 0.918f;
int rgb = Color.HSBtoRGB(hue, saturation, brightness);
int red = (rgb >> 16) & 0xFF;
int green = (rgb >> 8) & 0xFF;
int blue = rgb & 0xFF;
System.out.println((float)red / 255 + " " + (float)green / 255 + " " + (float)blue / 255);
}
}
Is there a typo in the wikipedia article ? Or what is missing in my implementation ?

You want the floating point modulus, but you're casting to an int (possibly to silence the compiler's complaints that % only works with integers).
In C++ it would be
std::abs(std::fmod(H_prime, 2) - 1);
(You will still see discrepancies, due to floating point imprecision.)

Related

Cylinder shape image using Jhlabs image processing library

I want to implement image processing in my project. I had used JLabs library. I want to do using this library following effects.
1) Deboss
2) Engrave
3) Satin etch
4) Custom shape like Cylinder.
This is sample image emboss code.
public class EmbossFilter
extends WholeImageFilter {
private static final float pixelScale = 255.9f;
private float azimuth = 2.3561945f;
private float elevation = 0.5235988f;
private boolean emboss = false;
private float width45 = 3.0f;
public void setAzimuth(float azimuth) {
this.azimuth = azimuth;
}
public float getAzimuth() {
return this.azimuth;
}
public void setElevation(float elevation) {
this.elevation = elevation;
}
public float getElevation() {
return this.elevation;
}
public void setBumpHeight(float bumpHeight) {
this.width45 = 3.0f * bumpHeight;
}
public float getBumpHeight() {
return this.width45 / 3.0f;
}
public void setEmboss(boolean emboss) {
this.emboss = emboss;
}
public boolean getEmboss() {
return this.emboss;
}
#Override
protected int[] filterPixels(int width, int height, int[] inPixels, Rect transformedSpace) {
int index = 0;
int[] outPixels = new int[width * height];
int bumpMapWidth = width;
int bumpMapHeight = height;
int[] bumpPixels = new int[bumpMapWidth * bumpMapHeight];
int i = 0;
while (i < inPixels.length) {
bumpPixels[i] = PixelUtils.brightness(inPixels[i]);
++i;
}
int Lx = (int) (Math.cos(this.azimuth) * Math.cos(this.elevation) * 255.89999389648438);
int Ly = (int) (Math.sin(this.azimuth) * Math.cos(this.elevation) * 255.89999389648438);
int Lz = (int) (Math.sin(this.elevation) * 255.89999389648438);
int Nz = (int) (1530.0f / this.width45);
int Nz2 = Nz * Nz;
int NzLz = Nz * Lz;
int background = Lz;
int bumpIndex = 0;
int y = 0;
while (y < height) {
int s1 = bumpIndex;
int s2 = s1 + bumpMapWidth;
int s3 = s2 + bumpMapWidth;
int x = 0;
while (x < width) {
int shade;
if (y != 0 && y < height - 2 && x != 0 && x < width - 2) {
int NdotL;
int Nx = bumpPixels[s1 - 1] + bumpPixels[s2 - 1] + bumpPixels[s3 - 1] - bumpPixels[s1 + 1] - bumpPixels[s2 + 1] - bumpPixels[s3 + 1];
int Ny = bumpPixels[s3 - 1] + bumpPixels[s3] + bumpPixels[s3 + 1] - bumpPixels[s1 - 1] - bumpPixels[s1] - bumpPixels[s1 + 1];
shade = Nx == 0 && Ny == 0 ? background : ((NdotL = Nx * Lx + Ny * Ly + NzLz) < 0 ? 0 : (int) ((double) NdotL / Math.sqrt(Nx * Nx + Ny * Ny + Nz2)));
} else {
shade = background;
}
if (this.emboss) {
int rgb = inPixels[index];
int a = rgb & -16777216;
int r = rgb >> 16 & 255;
int g = rgb >> 8 & 255;
int b = rgb & 255;
r = r * shade >> 8;
g = g * shade >> 8;
b = b * shade >> 8;
outPixels[index++] = a | r << 16 | g << 8 | b;
} else {
outPixels[index++] = -16777216 | shade << 16 | shade << 8 | shade;
}
++x;
++s1;
++s2;
++s3;
}
++y;
bumpIndex += bumpMapWidth;
}
return outPixels;
}
public String toString() {
return "Stylize/Emboss...";
}
}
How to achieve image effects using Jhlabs library or any other way please share with me.

Convert saturate_cast<uchar> in c++ to Java

I would like to know how to convert the following C++ code into java.
In particular, I need to convert RGB images into Opponent Space Images in order to extract the features for matching in my AR application.
void rgb2OpponentColorspace(Mat &bgrImage, vector<Mat> &opponentImageChannels)
{
if(bgrImage.type() != CV_8UC3)
{
cerr << "Error, attempting to convert to opponent space a non-BGR image!\n";
return;
}
//opponentImageChannels.resize(3);
opponentImageChannels.push_back(Mat(bgrImage.size(),CV_8UC1));
opponentImageChannels.push_back(Mat(bgrImage.size(),CV_8UC1));
opponentImageChannels.push_back(Mat(bgrImage.size(),CV_8UC1));
for(int y = 0; y < bgrImage.rows; ++y)
{
for(int x = 0; x < bgrImage.cols; ++x)
{
Vec3b v = bgrImage.at<Vec3b>(y, x);
uchar& b = v[0];
uchar& g = v[1];
uchar& r = v[2];
opponentImageChannels[0].at<uchar>(y, x) = saturate_cast<uchar>(0.5f * (255 + g - r)); // (R - G)/sqrt(2), but converted to the destination data type
opponentImageChannels[1].at<uchar>(y, x) = saturate_cast<uchar>(0.25f * (510 + r + g - 2*b)); // (R + G - 2B)/sqrt(6), but converted to the destination data type
opponentImageChannels[2].at<uchar>(y, x) = saturate_cast<uchar>(1.f/3.f * (r + g + b)); // (R + G + B)/sqrt(3), but converted to the destination data type
}
You may try implement this function by yourself, like this:
private final static int MAX_UCHAR = 255;
public static int saturateCastUchar(int x) {
return x > MAX_UCHAR ? MAX_UCHAR : (x < 0 ? 0 : x);
}
public static int saturateCastUchar(float x) {
return (int) (x > MAX_UCHAR ? MAX_UCHAR : (x < 0 ? 0 : x));
}
OpenCV for Java doesn't contain any ready-for-use function for this.
I have some discussion with my senior and we come out with the following solution.
opponentImageChannels.add(new Mat(mReferenceBGRImage.size(), CvType.CV_32FC1));
opponentImageChannels.add(new Mat(mReferenceBGRImage.size(), CvType.CV_32FC1));
opponentImageChannels.add(new Mat(bgrImage.size(), CvType.CV_32FC1));
for (int y = 0; y < bgrImage.rows(); ++y) {
for (int x = 0; x < bgrImage.cols(); ++x) {
double[] data = bgrImage.get(y, x);
double b = data[0];
double g = data[1];
double r = data[2];
opponentImageChannels.get(0).put(y, x, 0.5f * (255 + g + r));
opponentImageChannels.get(1).put(y, x, 0.25 * (510 + r + g - 2 * b));
opponentImageChannels.get(2).put(y, x, 0.5f * 1.0f / 3.0f * (r + g + b));
}
1) By using .add to replace .pushback
2) By using .get to replace .at
3) By using .put to place something into the channel

Encoding yuv frames to video file in java

I am trying to encode a video in java.
I have access to the separate frames as I420 yuv frames (these come from a different part of the program that I cannot change).
I basically have 3 bytebuffers for the different planes of a frame (+ dimensions).
As far as I understand, my format has 1 byte for the y-plane, and half a byte for u and v each, per pixel.
What is the best way to encode these into an mp4 video file?
I have tried with the xuggler API, but I can't seem to find a way to use the yuv frames directly.
Right now, I would convert them to a BufferedImage (TYPE_3BYTE_BGR) first before I can use them with the xuggler api to encode them to a video.
But this creates a huge overhead (I have to convert the yuv data to rgb for each pixel) and is unnecessary, as xuggler encodes them to yuv frames again to store them in a video file? (Not sure about this.)
So is there any easier way to encode raw yuv-frames to a video file directly in java?
Thanks for any pointers.
The way your are planning this seems correct (manual conversion), as long as xuggler didn't re-encode the frame after you.
I have done this conversion with both python and C, the process is still the same as yours (frame by frame, looping the pixels). In Java, this could look like :
public class YUV2RGB
{
public static void convert(int[] argb, byte[] yuv, int width, int height)
{
final int frameSize = width * height;
final int ii = 0;
final int ij = 0;
final int di = +1;
final int dj = +1;
int a = 0;
int y, v, u, r, g, b;
for (int i = 0, ci = ii; i < height; ++i, ci += di)
{
for (int j = 0, cj = ij; j < width; ++j, cj += dj)
{
y = (0xff & ((int) yuv[ci * width + cj]));
v = (0xff & ((int) yuv[frameSize + (ci >> 1) * width
+ (cj & ~1) + 0]));
u = (0xff & ((int) yuv[frameSize + (ci >> 1) * width
+ (cj & ~1) + 1]));
y = y < 16 ? 16 : y;
// METHOD 1 [slower, less accurate]
/*
* r = y + (int) 1.402f * v; g = y - (int) (0.344f * u + 0.714f
* * v); b = y + (int) 1.772f * u; r = r > 255 ? 255 : r < 0 ? 0
* : r; g = g > 255 ? 255 : g < 0 ? 0 : g; b = b > 255 ? 255 : b
* < 0 ? 0 : b; argb[a++] = 0xff000000 | (b<<16) | (g<<8) | r;
*/
// METHOD 2
r = (int) (1.164f * (y - 16) + 1.596f * (v - 128));
g = (int) (1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
b = (int) (1.164f * (y - 16) + 2.018f * (u - 128));
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
argb[a++] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
}
}
Sample code from https://github.com/jyanik/Mocobar/blob/master/Mocobar/src/com/yanik/mocobar/camera/YUV2RGB.java
I can´t remember the source of this code, probably it was from a question here in SO, but comes in handy here as it´s a version of the above using integer maths, I needed it for an Android project!
//Method from Ketai project! Not mine! See below...
void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0)
y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0) r = 0;
else if (r > 262143)
r = 262143;
if (g < 0) g = 0;
else if (g > 262143)
g = 262143;
if (b < 0) b = 0;
else if (b > 262143)
b = 262143;
rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}

Compute hex color code for an arbitrary string

Heading
Is there a way to map an arbitrary string to a HEX COLOR code. I tried to compute the HEX number for string using string hashcode. Now I need to convert this hex number to six digits which are in HEX color code range. Any suggestions ?
String [] programs = {"XYZ", "TEST1", "TEST2", "TEST3", "SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"};
for(int i = 0; i < programs.length; i++) {
System.out.println( programs[i] + " -- " + Integer.toHexString(programs[i].hashCode()));
}
If you don't really care about the "meaning" of the color you can just split up the bits of the int (remove the first for just RGB instead of ARGB)
String [] programs = {"XYZ", "TEST1", "TEST2", "TEST3", "SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"};
for(int i = 0; i < programs.length; i++) {
System.out.println( programs[i] + " -- " + intToARGB(programs[i].hashCode()));
}
....
public static String intToARGB(int i){
return Integer.toHexString(((i>>24)&0xFF))+
Integer.toHexString(((i>>16)&0xFF))+
Integer.toHexString(((i>>8)&0xFF))+
Integer.toHexString((i&0xFF));
}
How about anding the hashcode with 0x00FFFFFF (or 0xFFFFFF if you want to default the alpha channel)? For example:
private String getColorCode(String inputString)
{
String colorCode = String.format("#%06x", 0xFFFFFF & inputString.hashCode());
}
I ran into this question while looking for a Ruby solution, so I thought I would add an answer for Ruby in case someone follows the same path I did. I ended up using the following method, which creates the same six digit hex code from a string by using String.hash and the optional base-specifying parameter of Fixnum.to_s. It slices from 1 rather than 0 to skip negative signs.
def color_from_string(query)
'#'+query.hash.to_s(16).slice(1,6)
end
In case anyone else is looking for a solution for Flutter/Dart:
Color _fromInt(int i) {
final a = (i >> 24) & 0xFF;
final r = (i >> 16) & 0xFF;
final g = (i >> 8) & 0xFF;
final b = i & 0xFF;
return Color.fromARGB(a, r, g, b);
}
It's also worth noting that with certain background colours e.g. black, it may be difficult to differentiate the colours.
To this end, I set the alpha channel to the max value of 255:
Color _fromInt(int i) {
const a = 255;
final r = (i >> 16) & 0xFF;
final g = (i >> 8) & 0xFF;
final b = i & 0xFF;
return Color.fromARGB(a, r, g, b);
}
The following class takes a String and converts it to a color.
It is a simplified Java port of the Color-Hash TypeScript project (MIT license): https://github.com/zenozeng/color-hash.
The original project contains some parameters to adjust the generated colours.
These were not included.
The advantage of the Color-Hash algorithm, compared using a hash value directly, is that the generated colours are more perceptually uniform.
A lot of copy/paste was going on here:
Mostly from here the Color-Hash project (MIT license): https://github.com/zenozeng/color-hash
Convert HSL to RGB: https://stackoverflow.com/a/33947547/2021763
Easy way to convert Color to hex string: https://stackoverflow.com/a/18194652/2021763
Result:
XYZ: #bf40b3
TEST1: #86432d
TEST2: #3a2dd2
TEST3: #bf4073
SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS: #53ac8b
public class ColorHash
{
private static final double[] LigthnessArray = new double[] { 0.35, 0.5, 0.65 };
private static final double[] SaturationArray = new double[] { 0.35, 0.5, 0.65 };
public Color generateColor(String input) throws NoSuchAlgorithmException
{
// Easiest way would be String.hashCode()
// But "Test1" and "Test2" result in practically the same color
// Therefore another hash algorithm should be used
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(input.getBytes());
byte[] digest = md.digest();
int hash = Math.abs(ByteBuffer.wrap(digest).getInt());
double hue, saturation, lightness;
hue = hash % 359 / 359.; // note that 359 is a prime
hash = (int) Math.ceil(hash / 360);
saturation = SaturationArray[hash % SaturationArray.length];
hash = (int) Math.ceil(hash / SaturationArray.length);
lightness = LigthnessArray[hash % LigthnessArray.length];
return hslColor((float) hue, (float) saturation, (float) lightness);
}
public String generateColorHash(String input) throws NoSuchAlgorithmException
{
return "#" + Integer.toHexString(generateColor(input).getRGB()).substring(2);
}
private static Color hslColor(float h, float s, float l)
{
float q, p, r, g, b;
if (s == 0)
{
r = g = b = l; // achromatic
} else
{
q = l < 0.5 ? (l * (1 + s)) : (l + s - l * s);
p = 2 * l - q;
r = hue2rgb(p, q, h + 1.0f / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1.0f / 3);
}
return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255));
}
private static float hue2rgb(float p, float q, float h)
{
if (h < 0)
{
h += 1;
}
if (h > 1)
{
h -= 1;
}
if (6 * h < 1) { return p + ((q - p) * 6 * h); }
if (2 * h < 1) { return q; }
if (3 * h < 2) { return p + ((q - p) * 6 * ((2.0f / 3.0f) - h)); }
return p;
}
public static void main(String[] args) throws NoSuchAlgorithmException
{
String [] programs = {"XYZ", "TEST1", "TEST2", "TEST3", "SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"};
ColorHash gen = new ColorHash();
for(String p : programs) {
System.out.println(p + ": " + gen.generateColorHash(p));
}
}
}

Convert RGB value to HSV

I've found a method on the Internet to convert RGB values to HSV values.
Unfortunately, when the values are R=G=B, I'm getting a NaN, because of the 0/0 operation.
Do you know if there is an implemented method for this conversion in Java, or what do I have to do when I get the 0/0 division to get the right value of the HSV?
Here comes my method, adapted from some code on the Internet:
public static double[] RGBtoHSV(double r, double g, double b){
double h, s, v;
double min, max, delta;
min = Math.min(Math.min(r, g), b);
max = Math.max(Math.max(r, g), b);
// V
v = max;
delta = max - min;
// S
if( max != 0 )
s = delta / max;
else {
s = 0;
h = -1;
return new double[]{h,s,v};
}
// H
if( r == max )
h = ( g - b ) / delta; // between yellow & magenta
else if( g == max )
h = 2 + ( b - r ) / delta; // between cyan & yellow
else
h = 4 + ( r - g ) / delta; // between magenta & cyan
h *= 60; // degrees
if( h < 0 )
h += 360;
return new double[]{h,s,v};
}
I'm pretty sure what you want is RGBtoHSB
int r = ...
int g = ...
int b = ...
float[] hsv = new float[3];
Color.RGBtoHSB(r,g,b,hsv)
//hsv contains the desired values

Categories