I know the java code for grayscale is this( 0.2126 * red + 0.7152 * green + 0.0722 * blue(
I was wondering if anyone knows how I can find more variety of coloring formulas, like if i want to make the picture old fashion way, more orange, make it brighter, or darker ... sharper and so on
int pixel = image.getRGB(j, i);
int red = (pixel) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = (pixel >> 16) & 0xff;
int newPixel = (int) (0.2126 * red + 0.7152 * green + 0.0722 * blue);
image1.setRGB(j, i, newPixel);
The old fashion way you mention is called "sepia" effect. Take a look at this question particularly this answer which points out to the following code snippet (note that I did not write this code, just helping out in finding answers to your question)
/**
*
* #param img Image to modify
* #param sepiaIntensity From 0-255, 30 produces nice results
* #throws Exception
*/
public static void applySepiaFilter(BufferedImage img, int
sepiaIntensity) throws Exception
{
// Play around with this. 20 works well and was recommended
// by another developer. 0 produces a grey image
int sepiaDepth = 20;
int w = img.getWidth();
int h = img.getHeight();
WritableRaster raster = img.getRaster();
// We need 3 integers (for R,G,B color values) per pixel.
int[] pixels = new int[w*h*3];
raster.getPixels(0, 0, w, h, pixels);
// Process 3 ints at a time for each pixel. Each pixel has 3 RGB
colors in array
for (int i=0;i<pixels.length; i+=3)
{
int r = pixels[i];
int g = pixels[i+1];
int b = pixels[i+2];
int gry = (r + g + b) / 3;
r = g = b = gry;
r = r + (sepiaDepth * 2);
g = g + sepiaDepth;
if (r>255) r=255;
if (g>255) g=255;
if (b>255) b=255;
// Darken blue color to increase sepia effect
b-= sepiaIntensity;
// normalize if out of bounds
if (b<0) b=0;
if (b>255) b=255;
pixels[i] = r;
pixels[i+1]= g;
pixels[i+2] = b;
}
raster.setPixels(0, 0, w, h, pixels);
}
I would just play with the numbers.
more orange,
more red and a little more green (red + green = yellow)
brighter
increase all the factors
darker
decrease all the factors
sharper
This is specific filter which compare surrounding pixels to find edges. It not just a matter of playing with the colours.
BTW: You should add capping of the values. i.e. Math.min(255, Math.max(0, value))
You can manipulate the proportion between the color channels in order to change the scene "atmosphere". The images below were created using the ColorChannel plug-in.
The algorithm source code is presented below. The method getAttribute() gets the parameters (red,gree,blue) passed by the user. The methods getIntComponent0, getIntComponent1 and getIntComponent2 get each color channel (red, gree and blue). The method setIntColor sets back the value of each channel.
#Override
public void process
(
MarvinImage imageIn,
MarvinImage imageOut,
MarvinAttributes attrOut,
MarvinImageMask mask,
boolean preview
) {
int vr = (Integer)getAttribute("red");
int vg = (Integer)getAttribute("green");
int vb = (Integer)getAttribute("blue");
double mr = 1+Math.abs((vr/100.0)*2.5);
double mg = 1+Math.abs((vg/100.0)*2.5);
double mb = 1+Math.abs((vb/100.0)*2.5);
mr = (vr > 0? mr : 1.0/mr);
mg = (vg > 0? mg : 1.0/mg);
mb = (vb > 0? mb : 1.0/mb);
int red,green,blue;
for(int y=0; y<imageIn.getHeight(); y++){
for(int x=0; x<imageIn.getWidth(); x++){
red = imageIn.getIntComponent0(x, y);
green = imageIn.getIntComponent1(x, y);
blue = imageIn.getIntComponent2(x, y);
red = (int)Math.min(red * mr, 255);
green = (int)Math.min(green * mg, 255);
blue = (int)Math.min(blue * mb, 255);
imageOut.setIntColor(x, y, 255, red, green, blue);
}
}
}
Related
I want to convert a buffered image from RGBA format to CYMK format without using auto conversion tools or libraries,so i tried to extract the RGBA values from individual pixels that i got using BufferedImage.getRGB() and here what I've done so far :
BufferedImage img = new BufferedImage("image path")
int R,G,B,pixel,A;
float Rc,Gc,Bc,K,C,M,Y;
int height = img.getHeight();
int width = img.getWidth();
for(int y = 0 ; y < height ; y++){
for(int x = 0 ; x < width ; x++){
pixel = img.getRGB(x, y);
//I shifted the int bytes to get RGBA values
A = (pixel>>24)&0xff;
R = (pixel>>16)&0xff;
G = (pixel>>8)&0xff;
B = (pixel)&0xff;
Rc = (float) ((float)R/255.0);
Gc = (float) ((float)G/255.0);
Bc = (float) ((float)B/255.0);
// Equations i found on the internet to get CYMK values
K = 1 - Math.max(Bc, Math.max(Rc, Gc));
C = (1- Rc - K)/(1-K);
Y = (1- Bc - K)/(1-K);
M = (1- Gc - K)/(1-K);
}
}
Now after I've extracted it ,i want to draw or construct an image using theses values ,can you tell me of a method or a way to do this because i don't thinkBufferedImage.setRGB() would work ,and also when i printed the values of C,Y,M some of them hadNaN value can someone tell me what that means and how to deal with it ?
While it is possible, converting RGB to CMYK without a proper color profile will not produce the best results. For better performance and higher color fidelity, I really recommend using an ICC color profile (see ICC_Profile and ICC_ColorSpace classes) and ColorConvertOp. :-)
Anyway, here's how to do it using your own conversion. The important part is creating a CMYK color space, and a ColorModel and BufferedImage using that color space (you could also load a CMYK color space from an ICC profile as mentioned above, but the colors would probably look more off, as it uses different calculations than you do).
public static void main(String[] args) throws IOException {
BufferedImage img = ImageIO.read(new File(args[0]));
int height = img.getHeight();
int width = img.getWidth();
// Create a color model and image in CMYK color space (see custom class below)
ComponentColorModel cmykModel = new ComponentColorModel(CMYKColorSpace.INSTANCE, false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
BufferedImage cmykImg = new BufferedImage(cmykModel, cmykModel.createCompatibleWritableRaster(width, height), cmykModel.isAlphaPremultiplied(), null);
WritableRaster cmykRaster = cmykImg.getRaster();
int R,G,B,pixel;
float Rc,Gc,Bc,K,C,M,Y;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pixel = img.getRGB(x, y);
// Now, as cmykImg already is in CMYK color space, you could actually just invoke
//cmykImg.setRGB(x, y, pixel);
// and the method would perform automatic conversion to the dest color space (CMYK)
// But, here you go... (I just cleaned up your code a little bit):
R = (pixel >> 16) & 0xff;
G = (pixel >> 8) & 0xff;
B = (pixel) & 0xff;
Rc = R / 255f;
Gc = G / 255f;
Bc = B / 255f;
// Equations I found on the internet to get CMYK values
K = 1 - Math.max(Bc, Math.max(Rc, Gc));
if (K == 1f) {
// All black (this is where you would get NaN values I think)
C = M = Y = 0;
}
else {
C = (1- Rc - K)/(1-K);
M = (1- Gc - K)/(1-K);
Y = (1- Bc - K)/(1-K);
}
// ...and store the CMYK values (as bytes in 0..255 range) in the raster
cmykRaster.setDataElements(x, y, new byte[] {(byte) (C * 255), (byte) (M * 255), (byte) (Y * 255), (byte) (K * 255)});
}
}
// You should now have a CMYK buffered image
System.out.println("cmykImg: " + cmykImg);
}
// A simple and not very accurate CMYK color space
// Full source at https://github.com/haraldk/TwelveMonkeys/blob/master/imageio/imageio-core/src/main/java/com/twelvemonkeys/imageio/color/CMYKColorSpace.java
final static class CMYKColorSpace extends ColorSpace {
static final ColorSpace INSTANCE = new CMYKColorSpace();
final ColorSpace sRGB = getInstance(CS_sRGB);
private CMYKColorSpace() {
super(ColorSpace.TYPE_CMYK, 4);
}
public static ColorSpace getInstance() {
return INSTANCE;
}
public float[] toRGB(float[] colorvalue) {
return new float[]{
(1 - colorvalue[0]) * (1 - colorvalue[3]),
(1 - colorvalue[1]) * (1 - colorvalue[3]),
(1 - colorvalue[2]) * (1 - colorvalue[3])
};
}
public float[] fromRGB(float[] rgbvalue) {
// NOTE: This is essentially the same equation you use, except
// this is slightly optimized, and values are already in range [0..1]
// Compute CMY
float c = 1 - rgbvalue[0];
float m = 1 - rgbvalue[1];
float y = 1 - rgbvalue[2];
// Find K
float k = Math.min(c, Math.min(m, y));
// Convert to CMYK values
return new float[]{(c - k), (m - k), (y - k), k};
}
public float[] toCIEXYZ(float[] colorvalue) {
return sRGB.toCIEXYZ(toRGB(colorvalue));
}
public float[] fromCIEXYZ(float[] colorvalue) {
return sRGB.fromCIEXYZ(fromRGB(colorvalue));
}
}
PS: Your question talks about RGBA and CMYK, but your code just ignores the alpha value, so I did the same. If you really wanted to, you could just keep the alpha value as-is and have a CMYK+A image, to allow alpha-compositing in CMYK color space. I'll leave that as an exercise. ;-)
I'm trying to change the saturation of a particular image, in Java. I already know how to edit the hue and brightness of a pixel, but I'm stumped how to do saturation. Here's the loop I use to cycle through each of the pixels, if you need to know it. I know it isn't good for performance, but it's temporary.
Loop:
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
int pixel = image.getRGB(x, y);
int r = (pixel >> 16) & 0xFF;
int g = (pixel >> 8) & 0xFF;
int b = (pixel) & 0xFF;
//Adjust saturation:
//?????????????????????
}
}
In short, I'm not sure how to change the saturation of a pixel, but I want to know how. The loop I'm using above is working perfectly, so no problems there. Thanks! :D
You can use:
int red = ...;
int green = ...;
int blue = ...;
float[] hsb = Color.RGBtoHSB(red, green, blue, null);
float hue = hsb[0];
float saturation = hsb[1];
float brightness = hsb[2];
/* then change the saturation... */
int rgb = Color.HSBtoRGB(hue, saturation, brightness);
red = (rgb>>16)&0xFF;
green = (rgb>>8)&0xFF;
blue = rgb&0xFF;
I want to convert an image to a gray scale image where pixel intensities are between 0-255.
I was able to convert images to a gray scale images with the following Java method.
public void ConvertToGrayScale(BufferedImage bufImage, int ImgWidth, int ImgHeight) {
for (int w = 0; w < ImgWidth; w++) {
for (int h = 0; h < ImgHeight; h++) {
Color color = new Color(bufImage.getRGB(w, h));
int ColAvgVal = ((color.getRed() + color.getGreen() + color.getBlue()) / 3);
Color avg = new Color(ColAvgVal, ColAvgVal, ColAvgVal);
bufImage.setRGB(w, h, avg.getRGB());
System.out.println(avg.getRGB());
}
}
}
"System.out.println(avg.getRGB());" is used to see the pixel intensities but the all the grey levels are minus values and not between 0-255.
Am I doing it wrong ? How would I convert an image to a gray scale image where pixel intensities are between 0-255.
Thanks
color.getRGB() does not return a value from 0..255, it returns an integer composited of your red, green and blue values, including the Alpha value. Presumably, this alpha value is 0xFF, which makes any combined color end up as 0xFFrrggbb, or, as you got, a huge negative number when written in decimals.
To see the "gray" level assigned, just check ColAvgVal.
Note that a better formula to convert between RGB and grayscale is to use the PAL/NTSC conversion:
gray = 0.299 * red + 0.587 * green + 0.114 * blue
because "full blue" should be darker in grayscale than "full red" and "full green".
Note: if you use this formula directly, watch out for floating point rounding errors. In theory, it should not return a value outside of 0..255 for gray; in practice, it will. So test and clamp the result.
Another option which does not require testing-and-clamping per pixel, is to use an integer-only version:
gray = (299 * red + 587 * green + 114 * blue)/1000;
which should work with only a very small rounding error.
You can check this . I hope it can help you.
You can check some differents methods like:
// The average grayscale method
private static BufferedImage avg(BufferedImage original) {
int alpha, red, green, blue;
int newPixel;
BufferedImage avg_gray = new BufferedImage(original.getWidth(), original.getHeight(), original.getType());
int[] avgLUT = new int[766];
for(int i=0; i<avgLUT.length; i++)
avgLUT[i] = (int) (i / 3);
for(int i=0; i<original.getWidth(); i++) {
for(int j=0; j<original.getHeight(); j++) {
// Get pixels by R, G, B
alpha = new Color(original.getRGB(i, j)).getAlpha();
red = new Color(original.getRGB(i, j)).getRed();
green = new Color(original.getRGB(i, j)).getGreen();
blue = new Color(original.getRGB(i, j)).getBlue();
newPixel = red + green + blue;
newPixel = avgLUT[newPixel];
// Return back to original format
newPixel = colorToRGB(alpha, newPixel, newPixel, newPixel);
// Write pixels into image
avg_gray.setRGB(i, j, newPixel);
}
}
return avg_gray;
}
Im looking to add colour to a greyscale image; it doesnt have to be an accurate colour representation but adding a colour to a different shade of grey, this is just to identify different areas of interest within an image. E.g. areas of vegetation are likely to be of a similar shade of grey, by adding a colour to this range of values it should be clear which areas are vegetation, which are of water, etc.
I have the code for getting colours from an image and storing them as a colour object but this doesnt seem to give a way to modify the values.
E.g. if the grey vale is less than 85, colour red, if between 86 and 170 colour green and between 171 and 255 colour blue. I have no idea how this will look but in theory the resulting image should allow a user to identify the different areas.
The current code I have for getting pixel value is below.
int total_pixels = (h * w);
Color[] colors = new Color[total_pixels];
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
colors[i] = new Color(image.getRGB(x, y));
i++;
}
}
for (int i = 0; i < total_pixels; i++)
{
Color c = colors[i];
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
System.out.println("Red " + r + " | Green " + g + " | Blue " + b);
}
I appreciate any help at all! Thanks a lot
You are going to have to choose your own method of converting colours from the greyscale scheme to whatever colour you want.
In the example you've given, you could do something like this.
public Color newColorFor(int pixel) {
Color c = colors[pixel];
int r = c.getRed(); // Since this is grey, the G and B values should be the same
if (r < 86) {
return new Color(r * 3, 0, 0); // return a red
} else if (r < 172) {
return new Color(0, (r - 86) * 3, 0); // return a green
} else {
return new Color(0, 0, (r - 172) * 3); // return a blue
}
}
You may have to play around a bit to get the best algorithm. I suspect that the code above will actually make your image look quite dark and dingy. You'd be better off with lighter colours. For example, you might change every 0 in the code above to 255, which will give you shades of yellow, magenta and cyan. This is going to be a lot of trial and error.
I recommend you to take a look at Java2D. It has many classes that can make your life much easier. You may end up reinventing the wheel if you ignore it.
Here is a short showcase of what you can do:
int width = 100;
int height = 100;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
image.getRGB(x, y);
Graphics2D g2d = (Graphics2D)image.getGraphics();
g2d.setColor(Color.GREEN);
g2d.fillRect(x, y, width, height);
How to convert a white background of an image into a transparent background? Can anyone tel me how to do this?
The first result from Google is this:
Make a color transparent
http://www.rgagnon.com/javadetails/java-0265.html
It makes the Blue part of an image transparent, but I'm sure you can adapt that to use White intstead
(hint: Pass Color.WHITE to the makeColorTransparent function, instead of Color.BLUE)
Found a more complete and modern answer here: How to make a color transparent in a BufferedImage and save as PNG
This method will make background transparent. You need to pass the image you want to modify, colour, and tolerance.
final int color = ret.getRGB(0, 0);
final Image imageWithTransparency = makeColorTransparent(ret, new Color(color), 10);
final BufferedImage transparentImage = imageToBufferedImage(imageWithTransparency);
private static BufferedImage imageToBufferedImage(final Image image) {
final BufferedImage bufferedImage =
new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2 = bufferedImage.createGraphics();
g2.drawImage(image, 0, 0, null);
g2.dispose();
return bufferedImage;
}
private static Image makeColorTransparent(final BufferedImage im, final Color color, int tolerance) {
int temp = 0;
if (tolerance < 0 || tolerance > 100) {
System.err.println("The tolerance is a percentage, so the value has to be between 0 and 100.");
temp = 0;
} else {
temp = tolerance * (0xFF000000 | 0xFF000000) / 100;
}
final int toleranceRGB = Math.abs(temp);
final ImageFilter filter = new RGBImageFilter() {
// The color we are looking for (white)... Alpha bits are set to opaque
public int markerRGBFrom = (color.getRGB() | 0xFF000000) - toleranceRGB;
public int markerRGBTo = (color.getRGB() | 0xFF000000) + toleranceRGB;
public final int filterRGB(final int x, final int y, final int rgb) {
if ((rgb | 0xFF000000) >= markerRGBFrom && (rgb | 0xFF000000) <= markerRGBTo) {
// Mark the alpha bits as zero - transparent
return 0x00FFFFFF & rgb;
} else {
// Nothing to do
return rgb;
}
}
};
final ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
Here is my solution. This filter will remove the background from any image as long as the background image color is in the top left corner.
private static class BackgroundFilter extends RGBImageFilter{
boolean setUp = false;
int bgColor;
#Override
public int filterRGB(int x, int y, int rgb) {
int colorWOAlpha = rgb & 0xFFFFFF;
if( ! setUp && x == 0 && y == 0 ){
bgColor = colorWOAlpha;
setUp = true;
}
else if( colorWOAlpha == bgColor )
return colorWOAlpha;
return rgb;
}
}
Elsewhere...
ImageFilter bgFilter = new BackgroundFilter();
ImageProducer ip = new FilteredImageSource(image.getSource(), bgFilter);
image = Toolkit.getDefaultToolkit().createImage(ip);
I am aware that this question is over a decade old and that some answers have already been given. However, none of them is satisfactory if the pixels inside the image are the same color as the background. Let's take a practical example. Given these images:
both have a white background, but the white color is also inside the image to be cutout. In other words, the white pixels on the outside of the two pennants must become transparent, the ones on the inside must remain as they are. Add to this the complication that the white of the background is not perfectly white (due to jpeg compression), so a tolerance is needed. The issue can be made more complex by figures that are not only convex, but also concave.
I created an algorithm in Java that solves the problem very well, I tested it with the two figures shown here. The following code refers to the Java API of Codename One (https://www.codenameone.com/javadoc/), but can be repurposed to the Java SE API or implemented in other languages. The important thing is to understand the rationale.
/**
* Given an image with no transparency, it makes the white background
* transparent, provided that the entire image outline has a different color
* from the background; the internal pixels of the image, even if they have
* the same color as the background, are not changed.
*
* #param source image with a white background; the image must have an
* outline of a different color from background.
* #return a new image with a transparent background
*/
public static Image makeBackgroundTransparent(Image source) {
/*
* Algorithm
*
* Pixels must be iterated in the four possible directions: (1) left to
* right, for each row (top to bottom); (2) from right to left, for each
* row (from top to bottom); (3) from top to bottom, for each column
* (from left to right); (4) from bottom to top, for each column (from
* left to right).
*
* In each iteration, each white pixel is replaced with a transparent
* one. Each iteration ends when a pixel of color other than white (or
* a transparent pixel) is encountered.
*/
if (source == null) {
throw new IllegalArgumentException("ImageUtilities.makeBackgroundTransparent -> null source image");
}
if (source instanceof FontImage) {
source = ((FontImage) source).toImage();
}
int[] pixels = source.getRGB(); // array instance containing the ARGB data within this image
int width = source.getWidth();
int height = source.getHeight();
int tolerance = 1000000; // value chosen through several attempts
// check if the first pixel is transparent
if ((pixels[0] >> 24) == 0x00) {
return source; // nothing to do, the image already has a transparent background
}
Log.p("Converting white background to transparent...", Log.DEBUG);
// 1. Left to right, for each row (top to bottom)
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
// 2. Right to left, for each row (top to bottom)
for (int y = 0; y < height; y++) {
for (int x = width - 1; x >= 0; x--) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
// 3. Top to bottom, for each column (from left to right)
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
// 4. Bottom to top, for each column (from left to right)
for (int x = 0; x < width; x++) {
for (int y = height - 1; y >= 0; y--) {
int color = pixels[y * width + x];
if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
pixels[y * width + x] = 0x00; // means full transparency
} else {
break;
}
}
}
return EncodedImage.createFromRGB(pixels, width, height, false);
}