I am editing a BufferedImage.
After altering the pixel in the picture, I do a check to ensure the new value is what I expected it to be. However, they have not changed to the designated pixel Color!
I thought it could be something to do with the Alpha value, so I recently added a step that extracts the Alpha value from the original pixel, and ensures that value is used when creating the new Color to be inserted back into the image.
System.out.println(newColors[0] + ", " + newColors[1] + ", " + newColors[2]);
Color oldColor = new Color(image.getRGB(x, y));
Color newColor = new Color(newColors[0], newColors[1], newColors[2], oldColor.getAlpha()); // create a new color from the RGB values.
image.setRGB(x, y, newColor.getRGB());// set the RGB of the pixel in the image.
for (int col : getRGBs(x,y)) {
System.out.println(col);
}
The method getRGBs() returns an array where
index 0 is the Red value
index 1 is green
index 2 is blue.
The output looks like:
206, 207, 207
204
203
203
As you can see, the values 206, 207, 207 come back out of the image as 204, 203, 203 - in fact, every pixel I change comes back out as 204, 203, 203.
What am I doing wrong? It just doesn't make sense.
Thanks in advance!
I found my own answer online, I'll summarise it below:
In BufferedImages with a ColorModel the pixel is set to the nearest colour chosen. That means that you might not get the colour you wanted because the colours you can set are limited to the colours in the ColorModel.
You can get around that by creating your own BufferedImage and draw the source image onto that and then manipulate those pixels.
BufferedImage original = ImageIO.read(new File(file.getPath()));
image= new BufferedImage(original.getWidth(), original.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
image.getGraphics().drawImage(original, 0, 0, null);
for(int y = 0; y < original.getHeight(); y++){
for(int x = 0; x < original.getWidth(); x++){
image.setRGB(x,y, original.getRGB(x,y));
}
}
That solved the problem. Clearly, the ColorModel did not have the colours I had specified and thus adjusted the pixel to the nearest colour it could.
Source
I guess what you are looking for is WritableRaster which helps to write to the image read.
Use ImageIO to write the final changes on to a new file or give the same file for altering.
public class ImageTest {
BufferedImage image;
File imageFile = new File("C:\\Test\\test.bmp");
public ImageTest() {
try {
image = ImageIO.read(imageFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void editImage() throws IOException {
WritableRaster wr = image.getRaster();
int width = image.getWidth();
int height = image.getHeight();
for(int ii=0; ii<width; ii++) {
for(int jj=0; jj<height; jj++) {
int color = image.getRGB(ii, jj);
wr.setSample(ii, jj, 0 , 156);
}
}
ImageIO.write(image, "BMP", new File("C:\\Test\\test.bmp"));
}
public static void main(String[] args) throws IOException {
ImageTest test = new ImageTest();
test.editImage();
}
}
Related
What I need to do is I have a input image file and I need to change it as user's parameters. For example if user wants to make it %30 darker, first I get all pixels with their RGB values and store them in an 2D array. Sample array given below.
114 121 140 //Pixel 1
114 121 140 //Pixel 2
114 121 140 //Pixel 3
.
.
.
50 57 83 //Pixel 2073601
After that I overwrite that RGB values (in our case if RGB values are 10:10:10, new values will be 7:7:7). Everything from that point is ok. But now I'm facing some difficulties about creating my output.jpg file using my new array of information. When I run my function, it does not creates any output.jpg file. Here is my function.
public static void createFinalImage(int height, int width, String[][] rgbArray, String outputFile) {
BufferedImage img;
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
File f;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
String[] data = rgbArray[y][x].split(" ");
int red = Integer.parseInt(data[0]);
int green = Integer.parseInt(data[1]);
int blue = Integer.parseInt(data[2]);
int rgb = new Color(red,green,blue).getRGB();
img.setRGB(x,y,rgb);
}
}
try
{
f = new File(outputFile);
ImageIO.write(img, "jpg", f);
}
catch(IOException e)
{
System.out.println("Error: " + e);
}
}
I cant understand what is the problem and why I cant get darker image using this function. (I know there is always easier and basic ways to do it but this is an multithreading assignment and I must do it like I explained.) If anyone help me, I would be appreciated.
The ImageIO.write(...) methods return a boolean indicating whether or not there is a plugin installed that can write the image in the given format. It's good practice to check this value. While there is always a JPEG plugin installed, in recent versions of Java the JPEG plugin no longer supports images with alpha channel. Most other software don't support 4 channel RGBA JPEGs anyway, so it's not a big loss...
All you need is to change BufferedImage.TYPE_INT_ARGB to a type without alpha, like BufferedImage.TYPE_INT_RGB or TYPE_3BYTE_BGR, and things will work. You don't seem to use the alpha channel anyway.
The important changes:
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Manipulate pixels
}
}
try {
File f = new File(outputFile);
if (!ImageIO.write(img, "JPEG", f)) {
System.err.println("No plugin to write " + img + " in JPEG format to " + f);
}
}
catch (IOException e) {
System.out.println("Error: " + e);
}
As #HaraldK said, in my function I was using BufferedImage.TYPE_INT_ARGB as parameter. But since I have no alpha value, I just changed it as BufferedImage.TYPE_INT_RGB. Modified function given below and it works as normal.
public static void createFinalImage(int height, int width, String[][] rgbArray, String outputFile) {
BufferedImage img;
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
File f;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
String[] data = rgbArray[y][x].split(" ");
int red = Integer.parseInt(data[0]);
int green = Integer.parseInt(data[1]);
int blue = Integer.parseInt(data[2]);
int rgb = new Color(red,green,blue).getRGB();
img.setRGB(x,y,rgb);
}
}
try
{
f = new File(outputFile);
ImageIO.write(img, "jpg", f);
}
catch(IOException e)
{
System.out.println("Error: " + e);
}
}
I have a problem with getting gray scale of a .jpg file. I am trying to create a new .jpg file as gray scaled but I am just copying the image nothing more. Here is my code:
package training01;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class GrayScale {
BufferedImage image;
int width;
int height;
public GrayScale() {
try {
File input = new File("digital_image_processing.jpg");
image = ImageIO.read(input);
width = image.getWidth();
height = image.getHeight();
for(int i = width;i < width;i++) {
for(int j = height;j < height;j++) {
Color c = new Color(image.getRGB(i, j));
int red = c.getRed();
int green = c.getGreen();
int blue = c.getBlue();
int val = (red+green+blue)/3;
Color temp = new Color(val,val,val);
image.setRGB(i, j, temp.getRGB());
}
}
File output = new File("digital_image_processing1.jpg");
ImageIO.write(image, "jpg", output);
}catch(Exception e) {
System.out.println(e);
}
}
public static void main(String[] args) {
GrayScale gs = new GrayScale();
}
}
You need to change the following. Start your i and j at 0.
for(int i = width;i < width;i++) {
for(int j = height;j < height;j++) {
However, here is a faster way to do it. Write it to a new BufferedImage object that is set for gray scale.
image = ImageIO.read(input);
width = image.getWidth();
height = image.getHeight();
bwImage = new BufferedImage(width,
height, BufferedImage.TYPE_BYTE_GRAY);
Graphics g = bwImage.getGraphics();
g.drawImage(image,0,0,null);
Then save the bwImage.
The main problem with your code, is that it won't loop, because you initialize i, j to width, height which is already greater than the exit condition of the for loops (i < width, j < height). Start iterating at 0 by initializing i and j to 0, and your code will work as intended.
For better performance, you also want to change the order of the loops. As BufferedImages are stored as a continuous array, row by row, you will utilize the CPU cache much better if you loop over the x axis (row) in the inner loop.
Side note: I also suggest renaming i and j to x and y for better readability.
Finally, your method of converting RGB to gray by averaging the colors will work, but is not the most common way to convert to gray scale, as the human eye does not perceive the intensities of the colors as the same. See Wikipedia on gray scale conversion for a better understanding of correct conversion and the theory behind it.
However, all of this said, for JPEG images stored as YCbCr (the most common way to store JPEGs), there is a much faster, memory efficient and simpler way of converting the image to gray scale, and that is simply reading the Y (luminance) channel of the JPEG and use that as gray scale directly.
Using Java and ImageIO, you can do it like this:
public class GrayJPEG {
public static void main(String[] args) throws IOException {
try (ImageInputStream stream = ImageIO.createImageInputStream(new File(args[0]))) {
ImageReader reader = ImageIO.getImageReaders(stream).next(); // Will throw exception if no reader available
try {
reader.setInput(stream);
ImageReadParam param = reader.getDefaultReadParam();
// The QnD way, just specify the gray type directly
//param.setDestinationType(ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_BYTE_GRAY));
// The very correct way, query the reader if it supports gray, and use that
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
while (types.hasNext()) {
ImageTypeSpecifier type = types.next();
if (type.getColorModel().getColorSpace().getType() == ColorSpace.TYPE_GRAY) {
param.setDestinationType(type);
break;
}
}
BufferedImage image = reader.read(0, param);
ImageIO.write(image, "JPEG", new File(args[0] + "_gray.jpg"));
}
finally {
reader.dispose();
}
}
}
}
I have a program that is supposed to take the RGB values of an image and then multiply them by some constants, and then draw the new image on a JPanel. The problem is that if my image is over a certain height, specifically over 187 pixels, the new colored image is different than an image with a height of less than 187px.
The JPanel shows this: example.
Notice how the longer recolored image is different than the shorter one. I'm sure that the shorter image's colors are correct, and I have no idea how it's getting messed up.
public class RecolorImage extends JPanel {
public static int scale = 3;
public static BufferedImage walk, walkRecolored;
public static BufferedImage shortWalk, shortWalkRecolored;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(200*scale, 400*scale);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RecolorImage());
walk = ImageLoader.loadImage("/playerWalk.png");
walkRecolored = recolor(walk);
shortWalk = ImageLoader.loadImage("/playerWalkShort.png");
shortWalkRecolored = recolor(shortWalk);
frame.setVisible(true);
}
#Override
public void paint(Graphics graphics) {
Graphics2D g = (Graphics2D) graphics;
g.scale(scale, scale);
g.drawImage(walk, 10, 10, null);
g.drawImage(walkRecolored, 40, 10, null);
g.drawImage(shortWalk, 70, 10, null);
g.drawImage(shortWalkRecolored, 100, 10, null);
}
The recolor method:
public static BufferedImage recolor(BufferedImage image) {
BufferedImage outputImage = deepCopy(image);
for (int y = 0; y < image.getHeight(); y++) {
for (int x = 0; x < image.getWidth(); x++) {
int rgb = image.getRGB(x, y);
Color c = new Color(rgb);
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
r *= 0.791;
g *= 0.590;
b *= 0.513;
int newRGB = (rgb & 0xff000000) | (r << 16) | (g << 8) | b;
outputImage.setRGB(x, y, newRGB);
}
}
return outputImage;
}
How I load the images and make deep copies:
public static BufferedImage loadImage(String path) {
try {
return ImageIO.read(ImageLoader.class.getResource(path));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static BufferedImage deepCopy(BufferedImage image) {
ColorModel colorModel = image.getColorModel();
boolean isAlphaPremultiplied = colorModel.isAlphaPremultiplied();
WritableRaster raster = image.copyData(null);
return new BufferedImage(colorModel, raster, isAlphaPremultiplied, null);
}
My original images: the tall image and short image. Thanks for any help!
Your source images have different color models:
the short image uses 4 bytes per pixel (RGB and alpha)
the tall image uses 1 byte per pixel (index into a palette of 256 colors)
Your recolored images use the same color model as the source images (thanks to the deepCopy method), therefore the recolored image for the tall image also uses the same color palette as the source image, meaning that it cannot contain all the colors you want.
Since your recoloring code overwrites each pixel of the output image anyway the deep copy operation is unnecessary. Instead you would better create a full color image as target image like this:
public static BufferedImage recolor(BufferedImage image) {
BufferedImage outputImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
//... code as before
}
I'm trying to make a Mario game clone, and right now, in my constructor, I have a method that is supposed to make a certain color transparent instead of the current pinkish (R: 255, G: 0, B: 254). According to Photoshop, the hex value is ff00fe. My method is:
public Mario(){
this.state = MarioState.SMALL;
this.x = 54;
this.y = 806;
URL spriteAtLoc = getClass().getResource("sprites/Mario/SmallStandFaceRight.bmp");
try{
sprite = ImageIO.read(spriteAtLoc);
int width = sprite.getWidth();
int height = sprite.getHeight();
int[] pixels = new int[width * height];
sprite.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] == 0xFFff00fe) {
pixels[i] = 0x00ff00fe; //this is supposed to set alpha value to 0 and make the target color transparent
}
}
} catch(IOException e){
System.out.println("sprite not found");
e.printStackTrace();
}
}
it runs and compiles, but sprite comes out exactly the same when I render it. (edit: perhaps of note I do not have super.paintComponent(g) in my paintComponent(g) method. The sprites are .bmps.
You are only retrieving the pixels using BufferedImage.getRGB. That returns a copy of the data in a certain area of the BufferedImage.
Any change you make to the int[] returned is not automatically reflected back into the image.
To update the image, you need to call BufferedImage.setRGB after you change the int[]:
sprite.setRGB(0, 0, width, height, pixels, 0, width);
Another change you should probably make (and this involves a little guesswork as I don't have your bmp to test with) - the BufferedImage returned by ImageIO.read may have type BufferedImage.TYPE_INT_RGB - meaning that it doesn't have an alpha channel. You can verify by printing sprite.getType(), if that prints 1 it's TYPE_INT_RGB without an alpha channel.
To get an alpha channel, create a new BufferedImage of the right size and then set the converted int[] on that image, then use the new image from then on:
BufferedImage newSprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
newSprite.setRGB(0, 0, width, height, pixels, 0, width);
sprite = newSprite; // Swap the old sprite for the new one with an alpha channel
BMP images don't provide an alpha channel, you have to set it manually (as you do in your code)...
when you check your pixel to have a certain color you have to check without alpha (BMP has no alpha it's always 0x0).
if (pixels[i] == 0x00ff00fe) { //THIS is the color WITHOUT alpha
pixels[i] = 0xFFff00fe; //set alpha to 0xFF to make this pixel transparent
}
so in short: you did all right but mixed it up a bit ^^
This works:
private BufferedImage switchColors(BufferedImage img) {
int w = img.getWidth();
int h = img.getHeight();
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// top left pixel is presumed to be BG color
int rgb = img.getRGB(0, 0);
for (int xx=0; xx<w; xx++) {
for (int yy=0; yy<h; yy++) {
int rgb2 = img.getRGB(xx, yy);
if (rgb2!=rgb) {
bi.setRGB(xx, yy, rgb2);
}
}
}
return bi;
}
I am currently trying to read in an image pixel by pixel and change each colored pixel to the rgb value of (100,100,100). For whatever reason when I check the values of each pixel one the image is saved it has all the colored pixels as (46,46,46) instead.
Here is the original image
After running my program this is the image it gives to me
Here is the code
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class Cmaps {
public static void main(String[] args){
File originalImage = new File("C:\\Users\\Me\\Desktop\\0005.bmp");
BufferedImage img = null;
try{
img = ImageIO.read(originalImage);
for(int i = 0; i < img.getHeight(); i++){
for(int j = 0; j < img.getWidth(); j++){
//get the rgb color of the image and store it
Color c = new Color(img.getRGB(i, j));
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
//if the pixel is white then leave it alone
if((r == 255) && (g == 255) && (b == 255)){
img.setRGB(i, j, c.getRGB());
continue;
}
//set the colored pixel to 100,100,100
r = 100;// red component 0...255
g = 100;// green component 0...255
b = 100;// blue component 0...255
int col = (r << 16) | (g << 8) | b;
img.setRGB(i, j, col);
}
}
File f = new File("C:\\Users\\Me\\Desktop\\2\\1.png");
try {
ImageIO.write(img, "PNG", f);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e){
e.printStackTrace();
}
}
}
I have no clue why it doesn't set the pixels to the expected rgb value. I eventually want to be able to basically increment the rgb color as I move down rows and columns in the x and y so what the final image will look like is it will start off dark in the top left corner and then have a fade out effect as you get from that side to the bottom right corner.
Okay, based on the comments:
If the BufferedImage has an IndexColorModel (palette based color model), using setRGB to set a pixel to an arbitrary RGB value will not work. Instead, the color will be looked up, and the pixel will get the color that is considered the closest match in the palette.
Formats like BMP, GIF and PNG may all use IndexColorModel when read using ImageIO.
To convert the image to "true color" (either DirectColorModel or ComponentColorModel in Java will do), you can use:
BufferedImage img; // your original palette image
int w = img.getWidth();
int h = img.getHeight();
BufferedImage trueColor = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g = trueColor.createGraphics();
try {
g.drawImage(img, 0, 0, null);
}
finally {
g.dispose();
}
img = trueColor;
After this, getRGB(x, y) should return what you specify, using setRGB(x, y, argb).