I have a colour image that is been stored in a 3D array and now I want to display this data or the image on to a JPanel. My code read function is as follows :
public void readImage(String filename) throws Exception {
String filenameExtension = filename.substring(filename.lastIndexOf('.')+1);
File fileImage = FileChosen;
Iterator imageReaders = ImageIO.getImageReadersBySuffix(filenameExtension);
ImageReader imageReader;
if(imageReaders.hasNext())
imageReader = (ImageReader)imageReaders.next();
else
throw new IOException("Unsupported image format");
FileImageInputStream imageInputStream = new FileImageInputStream(fileImage);
imageReader.setInput(imageInputStream);
ImgWidth = imageReader.getWidth(0);
ImgHeight = imageReader.getHeight(0);
BufferedImage bufImage = imageReader.read(0);
imageInputStream.close();
WritableRaster wRaster = bufImage.getRaster();
//int numBands = wRaster.getNumBands();
System.out.println(ImgWidth);
System.out.println(ImgHeight);
imageArray = (new double[ImgHeight][ImgWidth][ColourLevels]);
// get the samples and normalize to between 0 and 1
for(int row = 0; row < ImgHeight; row++)
for(int col = 0; col < ImgWidth; col++)
for(int level = 0; level < ColourLevels; level++)
imageArray[row][col][level] = (wRaster.getSample((col), (row), level) / 255.0);
} // end read method`
Now I want to print the data stored in the imageArray[][][] in to a panel, how can I do that?
Related
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());
I spent the whole day trying to implement the "convolution algorithm" in Java, but this last does not seem to work properly with all kernels, it works great with the blur kernel with a factor of 1/9, but not with the other ones.
For example, if I use the {{0.1.0},{0,0,0},{0,0,0}} matrix which is supposed to shift the image up by 1 pixel, surprisingly, it stretches the image all the way down.
Example of what I get:
And here is the code:
public class ConvolutionTest {
static BufferedImage bfimg;
static BufferedImage outputimg;
static File output = new File("/home/bz/Pictures/Selection_003_mod");
static File input = new File("/home/bz/Pictures/Selection_003.png");
static WritableRaster wr;
static int tempColor;
static double [] newColor = {0,0,0};
static double red=0, green=0, blue=0;
static double sumR=0, sumG=0, sumB=0;
public static void main(String[] args) throws IOException {
int tempIcoor;
int tempJcoor;
double[][] matConv = {
{0d, 1d, 0d},
{0d, 0d, 0d},
{0d, 0d, 0d}
};
bfimg = ImageIO.read(input);
outputimg = bfimg;
wr = outputimg.getRaster();
for (int i = 1; i < bfimg.getHeight()-1; i++) {
for (int j = 1; j < bfimg.getWidth()-1; j++) {
tempIcoor = i - 1;
tempJcoor = j - 1;
for (int tempI = 0; tempI < 3; tempI++) {
for (int tempJ = 0; tempJ < 3; tempJ++) {
tempColor = bfimg.getRGB(tempJcoor, tempIcoor);
red = tempColor >> 16 & 0xff;
red = red * matConv[tempI][tempJ];
green = tempColor >> 8 & 0xff;
green = green * matConv[tempI][tempJ];
blue = tempColor & 0xff;
blue = blue * matConv[tempI][tempJ];;
sumR = red + sumR;
sumG = green + sumG;
sumB = blue + sumB;
tempJcoor++;
}
newColor[0] = sumR;
newColor[1] = sumG;
newColor[2] = sumB;
tempIcoor++;
tempJcoor=j-1;
}
wr.setPixel(j, i, newColor);
sumR=0;
sumG=0;
sumB=0;
}
}
ImageIO.write(sortie, "png", output);
}
}
With
outputimg = bfimg;
you are setting the output image to be the same as the input image. When you perform the convolution of the first row, then (as you said) the first row of pixels from the input image will be written into the the second row of the output image. But they are identical - so you end up with all rows of the output image being copies of the first row of the input image.
Just replace this line with
outputimg = new BufferedImage(
bfimg.getWidth(), bfimg.getHeight(),
BufferedImage.TYPE_INT_ARGB);
to create a new output image to write to.
By the way: All this is already available in the standard API. You might want to have a look at the classes related to http://docs.oracle.com/javase/7/docs/api/java/awt/image/ConvolveOp.html
I am new for java. I have 2D binary matrix with only 1s and 0s now. I want to save it as jpg image(black and white) with same width and height. How could I realize that? I tried the code below but failed, saying "java.lang.IllegalArgumentException: image == null!" Please help me with that or give me your better solution. Thank you very much.
public static void main(String[] args) throws IOException {
//result is double[25][33] binary matrix with only 1s and 0s;
int height=result.length;
int width=result[0].length;;
byte[] data = new byte[height*width];
int k=0;
for(int i = 0;i < height;i++){
for(int j = 0; j < width; j++){
data[k]=(byte)result[i][j];
k++;
}
System.out.print("\n");
}
InputStream input = new ByteArrayInputStream(data);
BufferedImage output = ImageIO.read(input);
ImageIO.write(ouput, "jpg", new File("c:/result.jpg"));
}
This is a simple example that creates a 30x30 checkered box:
public static void main(String... args) throws IOException {
int w = 30, h = 30;
// create the binary mapping
byte BLACK = (byte)0, WHITE = (byte)255;
byte[] map = {BLACK, WHITE};
IndexColorModel icm = new IndexColorModel(1, map.length, map, map, map);
// create checkered data
int[] data = new int[w*h];
for(int i=0; i<w; i++)
for(int j=0; j<h; j++)
data[i*h + j] = i%4<2 && j%4<2 || i%4>=2 && j%4>=2 ? BLACK:WHITE;
// create image from color model and data
WritableRaster raster = icm.createCompatibleWritableRaster(w, h);
raster.setPixels(0, 0, w, h, data);
BufferedImage bi = new BufferedImage(icm, raster, false, null);
// output to a file
ImageIO.write(bi, "jpg", new File("C:\\Users\\user\\Desktop\\test.jpg"));
}
EDIT:
For what you are doing you actually don't need to create your own ImageColorModel, you can use a built in type: BufferedImage.TYPE_BYTE_GRAY or TYPE_BYTE_BINARY. Here is a better example and shows how to use grayscale to get a checkered box:
public static void main(String... args) throws IOException {
int w = 40, h = 40, divs = 5;
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY);
WritableRaster raster = bi.getRaster();
for(int i=0; i<w; i++)
for(int j=0; j<h; j++)
raster.setSample(i,j,0,128+(int)(127*Math.sin(Math.PI*i/w*divs)*Math.sin(Math.PI*j/h*divs)));
ImageIO.write(bi, "jpg", new File("C:\\Users\\user\\Desktop\\test.jpg"));
}
JHeatChart does this job as well without you having to create a custom image library.
http://www.javaheatmap.com/
// Create some dummy data.
double[][] data = new double[][]{{3,2,3,4,5,6},
{2,3,4,5,6,7},
{3,4,5,6,7,6},
{4,5,6,7,6,5}};
// Step 1: Create our heat map chart using our data.
HeatChart map = new HeatChart(data);
// Step 2: Customise the chart.
map.setTitle("This is my heat chart title");
map.setXAxisLabel("X Axis");
map.setYAxisLabel("Y Axis");
// Step 3: Output the chart to a file.
map.saveToFile(new File("java-heat-chart.png"));
What you are essentially trying to do is make a heat map. And instead of a range of values ranging from 0 to whatever, you have a range of 0 and 1.
Replace double[][] data = new double[][](//etc); with your boolean array.
I try to make a BMP file from BufferedImage. Here is function with that I try to write the header and pixels in bmp file.
I have a mistake but I can't find that. I need your help.
static void writeToBMP(BufferedImage img, String name)
{
//File header
int bfType = 0x424d;
int bfSize = (img.getHeight()*img.getWidth()*3)+54; // File size
short bfReserved1 = 0; // Reserved
short bfReserved2 = 0;
int bfOfBytes = 54; // Header size
//Header info
int biSize = 40; // Header 2 size
int biWidth = img.getWidth(); // Width in pixels
int biHeight = img.getHeight(); // Height in pixels
short biPlanes = 1; // Nr of planes
short biBitCount = 24; // Nr bites per pixel
int biCompression = 0;
int biSizeImage = (img.getHeight()*img.getWidth()*3); // Image size
int biXPelsPerMeter = 0;
int biYPelsPerMeter = 0;
int biClrUsed = 0;
int biClrImportant = 0;
File file = new File(name);
try {
OutputStream stream = new FileOutputStream(file);
fOut = new DataOutputStream(stream);
fOut.writeShort(bfType);
fOut.writeInt(bfSize);
fOut.writeShort(bfReserved1);
fOut.writeShort(bfReserved2);
fOut.writeInt(bfOfBytes);
//Write Header Info
fOut.writeInt(biSize);
fOut.writeInt(biWidth);
fOut.writeInt(biHeight);
fOut.writeShort(biPlanes);
fOut.writeShort(biBitCount);
fOut.writeInt(biCompression);
fOut.writeInt(biSizeImage);
fOut.writeInt(biXPelsPerMeter);
fOut.writeInt(biYPelsPerMeter);
fOut.writeInt(biClrUsed);
fOut.writeInt(biClrImportant);
for(int x=0; x<img.getWidth(); x++)
{
for(int y=0; y<img.getHeight(); y++)
{
Color c = new Color(img.getRGB(x,y));
fOut.writeByte(c.getRed());
fOut.writeByte(c.getBlue());
fOut.writeByte(c.getGreen());
}
}
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
I tried to write only header, and header size = 54 bytes.
I don't know if I calculate correct the bfSize and biSizeImage.
Try using ImageIO
File outImage = new File(name);
ImageIO.write(img, "bmp", outImage);
To resolve my problem I need to use LITTLE_ENDIAN byte order. For this I use:
ByteBuffer buffer = ByteBuffer.allocate(54);
buffer.putInt(bfSize);
buffer.putShort(bfReserved1);
buffer.putShort(bfReserved2);
buffer.putInt(bfOfBytes);
buffer.putInt(biSize);
buffer.putInt(biWidth);
buffer.putInt(biHeight);
buffer.putShort(biPlanes);
buffer.putShort(biBitCount);
buffer.putInt(biSizeImage);
buffer.putInt(biXPelsPerMeter);
buffer.putInt(biYPelsPerMeter);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.flip();
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;
}