I've been trying to complete a method that saves Color[][] to jpg image files, however the solutions will result in the output file being rotate 90 degrees, I've tried looking for the problem but does not come obvious to me, as well as other people with similar solutions doesn't seem to have the same problems.
Any help is greatly appreciated!
private Color[][] image; // assume this field has already been populated
public void saveImage() {
BufferedImage saveImage = new BufferedImage(this.image.length,
this.image[0].length,
BufferedImage.TYPE_INT_RGB);
for (int row = 0; row < this.image.length; row++) {
for (int col = 0; col < this.image[row].length; col++) {
saveImage.setRGB(row, col, this.image[row][col].getRGB());
}
}
String fName = UIFileChooser.save();
if (fName==null){return;}
File toFile = new File(fName+".jpg");
try {
ImageIO.write(saveImage,"jpg", toFile);
}catch (IOException e){UI.println("File save error: "+e);}
}
thanks for the helps, it turns out I just had to flip the dimensions and x/y coordinates, below is the fixed version:
private Color[][] image; // assume this field has already been populated
public void saveImage() {
BufferedImage saveImage = new BufferedImage(this.image[0].length,
this.image.length,
BufferedImage.TYPE_INT_RGB);
for (int row = 0; row < this.image.length; row++) {
for (int col = 0; col < this.image[row].length; col++) {
saveImage.setRGB(col, row, this.image[row][col].getRGB());
}
}
String fName = UIFileChooser.save();
if (fName==null){return;}
File toFile = new File(fName+".jpg");
try {
ImageIO.write(saveImage,"jpg", toFile);
}catch (IOException e){UI.println("File save error: "+e);}
}
The signature of the BufferedImage.setRGB method is public void setRGB​(int x, int y, int rgb)
X comes first, then Y. row equals y and col equals x.
So change that line to:
saveImage.setRGB(col, row, this.image[row][col].getRGB());
Related
I am working on a project in which I am trying to store all the pixels of a jpg image in an array, and then using that array to write a copy of the original image in a different folder. However, I do not seem to be able to write the copy into the folder. I know that the issue has something to do with the copy of the image I created, since the writing works perfectly if I use the original image as the parameter in ImageIO.write, but i'm still not entirely sure what the issue is. Does anyone know what sort of error I may be encountering, and what I could do to fix it? (For additional context, I know that reading/writing can be implemented without using arrays, but I want to use arrays to implement this)
Code for reference:
package com.company;
import java.io.File;
import java.util.Scanner;
import java.util.concurrent.*;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class filereader {
public static void main(String[] args) throws IOException {
int width = 1536;
int height = 2048;
BufferedImage image = null;
BufferedImage newimage = null;
int[][] newarray = new int[height][width];
File f = null;
File g = null;
try {
System.out.println("Insert your file");
Scanner scanney = new Scanner(System.in);
String arg1 = scanney.next();
f = new File(arg1);
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
image = ImageIO.read(f);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
newarray[i][j] = image.getRGB(j, i);
}
}
} catch (IOException e) {
System.out.println("Error:" + e);
}
try {
newimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
System.out.println(newimage.getType());
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int newimagestuff = newarray[i][j];
newimage.setRGB(j, i, newimagestuff);
}
}
int i = 0;
g = new File("C:\\Users\\user1\\Documents\\File Reading\\Outputest.jpg");
ImageIO.write(newimage,"jpg", g);
System.out.println(newimage);
System.out.println(image);
} catch (IOException e) {
System.out.println("Error:" + e);
}
}
}
There are several issues with your code:
You create two new BufferedImages - the first one is completely useless as you are overwriting it in the next line with the result of ImageIO.read().
The second BufferedImage you create has the type BufferedImage.TYPE_INT_ARGB which is probably wrong as JPG images don't have an alpha channel (thats the "A" in the type). You rather should use the type of the image read in with ImageIO.
Also why do you copy pixel by pixel when you could read always a complete row - if it has to be a two-dimensional array for intermediate storage - or even the complete image at once - if a single-dimensional image would suffice. You can use public int[] getRGB​(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) and the matching setRGB method for this.
I think escaping the space in your file path might help. Like that:
g = new File("C:\\Users\\user1\\Documents\\File\ Reading\\Outputest.jpg");
I'm trying to create a function that gets a bitmap and destiny color and returns the colored bitmap (without using paint). I found few ways of doing it but nothing works like I want it to.
The closest solution I was able to find is:
public static Bitmap changeImageColor(Bitmap srcBmp, int dstColor) {
int width = srcBmp.getWidth();
int height = srcBmp.getHeight();
float srcHSV[] = new float[3];
float dstHSV[] = new float[3];
Bitmap dstBitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
Color.colorToHSV(srcBmp.getPixel(col, row), srcHSV);
Color.colorToHSV(dstColor, dstHSV);
// If it area to be painted set only value of original image
dstHSV[2] = srcHSV[2]; // value
int color2=Color.HSVToColor(dstHSV);;
dstBitmap.setPixel(col, row, Color.HSVToColor(dstHSV));
}
}
return dstBitmap;
}
but It doesn't work very well on transparent images as can be seen here (before and after):
Anyone has any other solutions (again without using paint at all)?
You just need to extract alpha and re-apply it after transformation. And use ARGB_8888;
Edited your code to include alpha:
public Bitmap colorize(Bitmap srcBmp, int dstColor) {
int width = srcBmp.getWidth();
int height = srcBmp.getHeight();
float srcHSV[] = new float[3];
float dstHSV[] = new float[3];
Bitmap dstBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int pixel = srcBmp.getPixel(col, row);
int alpha = Color.alpha(pixel);
Color.colorToHSV(pixel, srcHSV);
Color.colorToHSV(dstColor, dstHSV);
// If it area to be painted set only value of original image
dstHSV[2] = srcHSV[2]; // value
dstBitmap.setPixel(col, row, Color.HSVToColor(alpha, dstHSV));
}
}
return dstBitmap;
}
here is a sample code for change the color for a bitmap:
private BitmapDrawable getColoredBitmap(int color, Context context,
int drawableId) {
Bitmap source = BitmapFactory.decodeResource(context.getResources(),
drawableId);
final Bitmap bitmap = Bitmap.createBitmap(source.getWidth(),
source.getHeight(), Bitmap.Config.ARGB_8888);
for (int i = 0; i < source.getWidth(); i++) {
for (int j = 0; j < source.getHeight(); j++) {
int pixel = source.getPixel(i, j);
// if (pixel == Color.TRANSPARENT) {
//
// } else
if (pixel == Color.WHITE) {
pixel = Color.argb(Color.alpha(pixel),
Color.red(Color.WHITE), Color.green(Color.WHITE),
Color.blue(Color.WHITE));
} else {
pixel = Color.argb(Color.alpha(pixel), Color.red(color),
Color.green(color), Color.blue(color));
}
bitmap.setPixel(i, j, pixel);
}
}
return new BitmapDrawable(context.getResources(), bitmap);
}
You do this:
int alpha=srcBmp.getPixel(col, row);
dstBitmap.setPixel(col, row, Color.HSVToColor(dstHSV));
in which you calculate an alpha (probably incorrectly from the looks of that code) and then don't use it. You are probably going to have to create a Color with HSVToColor, then set the alpha of that color, then use it in setPixel. And you are probably going to have to get the alpha in a similar way because I find it hard to believe a getPixel function only returns the alpha :p
What I'm trying to do
Convert my sprite sheet into separate buffered images, of which, each will be stored in a an array. The way I'm doing is from the top, down and from left to right.
The issue:
When I go through all the elements and get to the 1st element of the next row. The image is blank, however, all elements following show as intended.
Illustration:
Note: The location is the same for each element.
Sprite sheet:
Link: http://imageshack.us/photo/my-images/153/ljsh.png/
As it appears:
Link: "http://imageshack.us/photo/my-images/194/t3be.png/"
Anyone can use the sprite sheet if they want to.
Need more reputation for the images. :/
Code:
Note: I'm still very new to Java, so please, be gentle ;)
I think the problem is within the for loop that's in the loadSpriteSheet method.
Although what the cause is exactly, I have no idea.
How it works, from my understanding is that:
Loads sprite sheet image.
Array of buffered images is created based on the amount of rows and columns input by user.
The size is calculated SizeX = image.width / columns, SizeY = image.height / rows.
Graphics object draws to the current element in the array.
The sprite sheet is moved -SizeX.
The next element is drawn.
Repeats until x = columns.
Starts next row, sprite sheet is moved -SizeY.
Continues the same as before.
The "System.out" part shows the location of tX and tY exactly where I would expect them to be.
public class SpriteSheet extends ImageObject {
// Variables
protected BufferedImage[] spriteSheet;
protected AffineTransform sprSheetAt;
// Getters
public int getLength() {return spriteSheet.length;}
// Setters
// Constructor
public SpriteSheet(JFrame a) {
super(a);
}
public void loadSpriteSheet(String filename, int rows, int columns) {
try {
image = ImageIO.read(new FileInputStream(filename));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
sprSheetAt = AffineTransform.getTranslateInstance(0, 0);
// Size of sprite image.
int spriteSX = image.getWidth() / columns;
int spriteSY = image.getHeight() / rows;
int tX = 0;
int tY = 0;
spriteSheet = new BufferedImage[rows * columns];
int spriteInd = 0;
for(int y = 0; y < rows; y++) {
for(int x = 0; x < columns; x++) {
Graphics2D g;
if(spriteInd >= spriteSheet.length) {
break;
}
spriteSheet[spriteInd] = new BufferedImage(spriteSX, spriteSY, BufferedImage.TYPE_INT_ARGB);
g = spriteSheet[spriteInd].createGraphics();
g.drawImage(image, sprSheetAt, null);
g.dispose();
System.out.println(spriteInd + ", tX: " + tX + ", tY: " + tY);
tX -= spriteSX;
spriteInd++;
sprSheetAt.setToTranslation(tX, tY);
}
tX = 0;
tY -= spriteSY;
}
}
public void drawSprite(Graphics2D g, int spriteInd) {
g2d = g;
if(alive) {
g2d.drawImage(spriteSheet[spriteInd], 0, 0, frame);
}
}
}
I hope I explained this clearly, my apologies if I have not.
Thank you in advanced for the help.
Upon looking again, at other sprite/sprite sheet related questions, I came across BufferedImage.getSubimage. (I noticed just before I posted this that it was suggested)
And thus I immediately implemented it.
I changed the inner most for loop that's within the loadSpriteSheet method to.
for(int y = 0; y < rows; y++) {
for(int x = 0; x < columns; x++) {
spriteSheet[spriteInd] = new BufferedImage(spriteSX, spriteSY, BufferedImage.TYPE_INT_ARGB);
System.out.println(spriteInd + ", tX: " + tX + ", tY: " + tY);
spriteSheet[spriteInd] = image.getSubimage(tX, tY, spriteSX, spriteSX);
tX += spriteSX;
spriteInd++;
}
tX = 0;
tY += spriteSY;
}
It's much shorter and actually works now :D
So from this I have learned a few things.
1. Do more googling and general searching.
2. Check the api more.
3. And wait until I've spent a long time searching to ask :)
Despite this, I would like to know what the cause of the issue was.
I have this kind of method:
public void SaveImageOntoObject(String filepath) throws IOException {
BufferedImage image = ImageIO.read(getClass().getResourceAsStream(filepath));
this.width = image.getWidth();
this.height = image.getHeight();
this.ResetPointInformation();
for (int row = 0; row < width; row++) {
for (int col = 0; col < height; col++) {
this.PointInformation[row][col] = new Color(image.getRGB(col, row));
}
}
}
It takes the filepath of an image as input, converts the RPG Value of each pixel into a Color Object and then stores it into the two-dimensional Array PointInformation of the Object the Method was called onto.
Now to my problem:
While some pictures like this one:
Work like a charm, others like this:
Let me end up with the error:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Coordinate out of bounds!
at sun.awt.image.ByteInterleavedRaster.getDataElements(ByteInterleavedRaster.java:318)
at java.awt.image.BufferedImage.getRGB(BufferedImage.java:888)
at Drawing.Object2D.SaveImageOntoObject(Object2D.java:75)** (that's the class whose object's my method works on)
Why is that like that? It seems like Java is not able to convert certain RGB values into Colors?
Could you tell me how I can make it work?
The error message actually says it: "index out of bounds". It seems, that you confused your coordinates and their bounds. getRGB takes the parameters x (range 0 .. width) as first, and y (range 0 .. height) as second.
this.width = image.getWidth();
this.height = image.getHeight();
for (int row = 0; row < height; row++) { // swapped the ...
for (int col = 0; col < width; col++) { // ... bounds
this.PointInformation[row][col] = new Color(image.getRGB(col, row));
}
}
Your first example has width = height, so that the problem doesn't show.
Using the ImageJ api, I'm trying to save an composite image, made up of several images laid out side by side.
I've got code that loads ImagePlus objs, and saves them. But I can't figure how to paste an image into another image.
I interpret the problem as taking multiple images and stitching them together side by side to form a large one where the images may have different dimensions. The following incomplete code is one way of doing it and should get you started.
public ImagePlus composeImages(ArrayList<ImagePlus> imageList){
int sumWidth = 0;
int maxHeight = 0;
for(ImagePlus imp : imageList){
sumWidth = sumWidth +imp.getWidth();
if(imp.getHeight() > maxHeight)
maxHeight = imp.getWidth();
}
ImagePlus impComposite = new ImagePlus();
ImageProcessor ipComposite = new ShortProcessor(sumWidth, maxHeight);
for(int i=0; i<sumWidth; i++){
for(int j=0; j<sumWidth; j++){
ipComposite.putPixelValue(i, j, figureOutThis);
}
}
impComposite.setProcessor(ipComposite);
return impComposite;
}
You need to write an algorithm to find the pixel value (figureOutThis) to put in the composite image at i,j. That is pretty trivial if all images have the same width and a little bit more work otherwise. Happy coding
Edit:
I should add that I am assuming they are also all short images (I work with medical grayscale). You can modify this for other processors
This code combines/stitches a grid of images:
It assumes the images are all of the same dimensions.
ImagePlus combine(List<List<ImagePlus>> imagesGrid) {
checkArgument(
!imagesGrid.isEmpty() && !imagesGrid.get(0).isEmpty(), "Expected grid to be non-empty");
checkArgument(
imagesGrid.stream().map(List::size).distinct().count() == 1,
"Expected all rows in the grid to be of the same size");
checkArgument(
imagesGrid.stream().flatMap(List::stream).map(ImagePlus::getWidth).distinct().count() == 1,
"Expected all images to have the same width");
checkArgument(
imagesGrid.stream().flatMap(List::stream).map(ImagePlus::getHeight).distinct().count() == 1,
"Expected all images to have the same height");
int rows = imagesGrid.size();
int cols = imagesGrid.get(0).size();
int singleWidth = imagesGrid.get(0).get(0).getWidth();
int singleHeight = imagesGrid.get(0).get(0).getHeight();
int combinedWidth = singleWidth * cols;
int combinedHeight = singleHeight * rows;
ImageProcessor processor = new ColorProcessor(combinedWidth, combinedHeight);
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
ImagePlus image = imagesGrid.get(row).get(col);
int offSetWidth = col * singleWidth;
int offsetHeight = row * singleHeight;
for (int w = 0; w < singleWidth; w++) {
for (int h = 0; h < singleHeight; h++) {
processor.putPixel(w + offSetWidth, h + offsetHeight, image.getPixel(w, h));
}
}
}
}
ImagePlus combinedImage = new ImagePlus();
combinedImage.setProcessor(processor);
return combinedImage;
}