I am attempting to draw a custom polygon onto an image I have called in using imageIO. once the polygon has been added, the image should then be output.
Below is my code:
public static void setPoints(List<Integer> pointArrayX, List<Integer> pointArrayY,File dest) throws IOException{
BufferedImage image = ImageIO.read(new File(dest+""));
Graphics2D g2d = image.createGraphics();
g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
g2d.setColor(Color.RED);
BasicStroke bs = new BasicStroke(2);
g2d.setStroke(bs);
int[] xPoly = new int[pointArrayX.size()];
int[] yPoly = new int[pointArrayY.size()];
Polygon poly = new Polygon(xPoly,yPoly,xPoly.length);
poly.getBounds();
g2d.setPaint(Color.RED);
g2d.drawPolygon(poly);
g2d.fillPolygon(xPoly, yPoly, xPoly.length);
g2d.drawPolygon(xPoly, yPoly, xPoly.length);
g2d.setStroke(bs);
g2d.drawPolyline(xPoly, yPoly, xPoly.length);
g2d.drawOval(100, 100, 200, 200);
g2d.draw(poly);
File outputfile = new File(dest+"");
ImageIO.write(image, "png", outputfile);
Once run, the only shape which appears in the output image is the Oval which I have defined. it is just the Polygon which does not appear.
You don't populate your arrays.
int[] xPoly = new int[pointArrayX.size()]; //create an array and set its size
int[] yPoly = new int[pointArrayY.size()]; //create an array and set its size
Polygon poly = new Polygon(xPoly,yPoly,xPoly.length); //use the created array
You need to add data to xPoly and yPoly. Try:
int[] xPoly = new int[pointArrayX.size()]; //create an array and set its size
int[] yPoly = new int[pointArrayY.size()]; //create an array and set its size
//loop i added to copy the elements from your method arguments to the new arrays
for(int i = 0; i < xPoly.size(); i++) {
xPoly[i] = pointArrayX.get(i);
yPoly[i] = pointArrayY.get(i);
}
Polygon poly = new Polygon(xPoly,yPoly,xPoly.length); //use the created array
Related
I want to crop an image which is rectangular , into a circle of specific diameter. I am able to do it through graphics2D, and I get the image saved, but, when I read it through ImagIO, i get the full image again inspite to it being cropped to a circle. the image is a masked circle, and evrything outside is discarded like a mask. I am attaching the image here. inspite of it being clipped, i get the full image rendered, when i read it through imageIO. here is the code.
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
Area areaOval = new Area(new Arc2D.Double(0, 0, w, w, 0, 360,
Arc2D.PIE));
Shape shapeClipSave = g2.getClip();
g2.setClip(areaOval);
g2.drawImage(bufferedImage, 0, 0, null);
g2.setClip(shapeClipSave);
bufferedImage=output;
try {
ImageIO.write(bufferedImage,"png", new File("D:/new.png"));
bufferedImage= ImageIO.read(new File("D:/new.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g2.dispose();
Here's my take. I rewrote some parts for performance and better fidelity (I couldn't get the edges of the circular area antialiased using clip). Although your code should also work, in general.
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
// Remove odd borders (imgur issue?)... Remove this if your input doesn't have borders
image = image.getSubimage(10, 0, image.getWidth() - 20, image.getHeight() - 10);
int w = image.getWidth();
int h = image.getHeight();
image = createCircular(image, Math.min(w, h));
if (!ImageIO.write(image, "png", new File("new.png"))) {
System.err.println("Could not write PNG format");
System.exit(1);
}
image = ImageIO.read(new File("new.png"));
showItAll(image);
}
private static BufferedImage createCircular(BufferedImage image, int size) {
BufferedImage output = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.fillOval(0, 0, size, size);
g2.setComposite(AlphaComposite.SrcIn);
g2.drawImage(image, 0, 0, null);
}
finally {
g2.dispose();
}
return output;
}
private static void showItAll(BufferedImage image) {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(new JPanel() {
{
setBackground(Color.ORANGE);
}
});
frame.getContentPane().add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
Using your giraffe as input, I got the following output, the orange background is just to make the transparent parts clearly visible:
Alternatively, if you use the TwelveMonkeys library and the Adobe Path Support module, you can replace:
image = createCircular(image, Math.min(w, h));
with the following:
int size = Math.min(w, h);
image = Paths.applyClippingPath(new Ellipse2D.Float(0, 0, 1, 1),
image.getSubimage(0, 0, size, size));
Just be aware that the shape coordinates are relative to the size of the image, not in pixels.
Motivation:
My goal is to convert AWT BufferedImage to SWT ImageData in the most efficient way. Typical answer to this question is pixel by pixel conversion of the whole picture, that is O(n^2) complexity. Much more efficient would be if they could exchange whole pixel matrix as it is. BufferedImage seems to be very flexible in determining in detail how colors and alpha are encoded.
To provide you with a wider context, I wrote a SVG icon on demand rasterizer, using Apache Batik, but it is for SWT (Eclipse) application. Batik renders only to a java.awt.image.BufferedImage, but SWT components require org.eclipse.swt.graphics.Image.
Their backing raster objects: java.awt.image.Raster and org.eclipse.swt.graphics.ImageData represent exactly same thing, they are just wrapper around a 2D array of byte values representing pixels. If I can make one or the other to use came color encoding, voila, I can reuse the backing array as it is.
I got pretty far, this works:
// defined blank "canvas" for Batik Transcoder for SVG to be rasterized there
public BufferedImage createCanvasForBatik(int w, int h) {
new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
}
// convert AWT's BufferedImage to SWT's ImageData to be made into SWT Image later
public ImageData convertToSWT(BufferedImage bufferedImage) {
DataBuffer db = bufferedImage.getData().getDataBuffer();
byte[] matrix = ((DataBufferByte) db).getData();
PaletteData palette =
new PaletteData(0x0000FF, 0x00FF00, 0xFF0000); // BRG model
// the last argument contains the byte[] with the image data
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
ImageData swtimgdata = new ImageData(w, h, 32, palette);
swtimgdata.data = matrix; // ImageData has all field public!!
// ImageData swtimgdata = new ImageData(w, h, 32, palette, 4, matrix); ..also works
return swtimgdata;
}
It all works except transparency :(
It looks like ImageData requires (always?) alpha to be a separate raster, see ImageData.alphaData from color raster, see ImageData.data; both are byte[] types.
Is there a way how to make ImageData to accept ARGB model? That is alpha mixed with other colors? I doubt so I went the other way. To make BufferedImage to use separate arrays (aka rasters or "band") for colors and alpha. The ComponentColorModel and BandedRaster seem to intended exactly for these things.
So far I got here:
public BufferedImage createCanvasForBatik(int w, int h) {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
int[] nBits = {8, 8, 8, 8}; // ??
ComponentColorModel colorModel = new ComponentColorModel(cs, nBits, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createBandedRaster(
DataBuffer.TYPE_BYTE, w, h, 4, new Point(0,0));
isPremultiplied = false;
properties = null;
return new BufferedImage(colorModel, raster, isPremultiplied, properties);
}
That creates a separate raster (band) for alpha but also for every color separately, so I end up with 4 bands (4 rasters) which is again unusable for SWT Image. Is it possible to create a banded raster with 2 bands: one for colors in RGB or BRG, and one for alpha only?
I don't know SWT in detail, but based on my understand of the API doc, the below should work:
The trick is to use a custom DataBuffer implementation that masquerades as a "banded" buffer, but internally uses a combination of interleaved RGB and separate alpha array for storage. This works nicely with the standard BandedSampleModel. You will lose any chance of special (hardware) optimizations that are normally applied to BufferedImages using this model, but that should not matter as you are using SWT for display anyway.
I suggest you create your SWT image first, and then "wrap" the color and alpha arrays from the SWT image in the custom data buffer. If you do it this way, Batik should render directly to your SWT image, and you can just throw away the BufferedImage afterwards (if this is not practical, you can of course do it the other way around as well, but you may need to expose the internal arrays of the custom data buffer class below, to create the SWT image).
Code (important parts are the SWTDataBuffer class and createImage method):
public class SplitDataBufferTest {
/** Custom DataBuffer implementation using separate arrays for RGB and alpha.*/
public static class SWTDataBuffer extends DataBuffer {
private final byte[] rgb; // RGB or BGR interleaved
private final byte[] alpha;
public SWTDataBuffer(byte[] rgb, byte[] alpha) {
super(DataBuffer.TYPE_BYTE, alpha.length, 4); // Masquerade as banded data buffer
if (alpha.length * 3 != rgb.length) {
throw new IllegalArgumentException("Bad RGB/alpha array lengths");
}
this.rgb = rgb;
this.alpha = alpha;
}
#Override
public int getElem(int bank, int i) {
switch (bank) {
case 0:
case 1:
case 2:
return rgb[i * 3 + bank];
case 3:
return alpha[i];
}
throw new IndexOutOfBoundsException(String.format("bank %d >= number of banks, %d", bank, getNumBanks()));
}
#Override
public void setElem(int bank, int i, int val) {
switch (bank) {
case 0:
case 1:
case 2:
rgb[i * 3 + bank] = (byte) val;
return;
case 3:
alpha[i] = (byte) val;
return;
}
throw new IndexOutOfBoundsException(String.format("bank %d >= number of banks, %d", bank, getNumBanks()));
}
}
public static void main(String[] args) {
// These are given from your SWT image
int w = 300;
int h = 200;
byte[] rgb = new byte[w * h * 3];
byte[] alpha = new byte[w * h];
// Create an empty BufferedImage around the SWT image arrays
BufferedImage image = createImage(w, h, rgb, alpha);
// Just to demonstrate that it works
System.out.println("image: " + image);
paintSomething(image);
showIt(image);
}
private static BufferedImage createImage(int w, int h, byte[] rgb, byte[] alpha) {
DataBuffer buffer = new SWTDataBuffer(rgb, alpha);
// SampleModel sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, w, h, 4); // If SWT data is RGB, you can use simpler constructor
SampleModel sampleModel = new BandedSampleModel(DataBuffer.TYPE_BYTE, w, h, w,
new int[] {2, 1, 0, 3}, // Band indices for BGRA
new int[] {0, 0, 0, 0});
WritableRaster raster = Raster.createWritableRaster(sampleModel, buffer, null);
ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
return new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
}
private static void showIt(final BufferedImage image) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JLabel label = new JLabel(new ImageIcon(image));
label.setOpaque(true);
label.setBackground(Color.GRAY);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private static void paintSomething(BufferedImage image) {
int w = image.getWidth();
int h = image.getHeight();
int qw = w / 4;
int qh = h / 4;
Graphics2D g = image.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.ORANGE);
g.fillOval(0, 0, w, h);
g.setColor(Color.RED);
g.fillRect(5, 5, qw, qh);
g.setColor(Color.WHITE);
g.drawString("R", 5, 30);
g.setColor(Color.GREEN);
g.fillRect(5 + 5 + qw, 5, qw, qh);
g.setColor(Color.BLACK);
g.drawString("G", 5 + 5 + qw, 30);
g.setColor(Color.BLUE);
g.fillRect(5 + (5 + qw) * 2, 5, qw, qh);
g.setColor(Color.WHITE);
g.drawString("B", 5 + (5 + qw) * 2, 30);
g.dispose();
}
}
I currently have a one-dimensional double array holding 50 different points meant to be spaced 1 apart. I need these points to be drawn and connected by lines in an image. Currently the PNG image is being produced, and if I add in an individual line it will work, but somehow the loop makes the entire image come up as black. Any ideas on what's going wrong?
BufferedImage bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig2 = bi.createGraphics();
ig2.setBackground(Color.white);
ig2.setColor(Color.red);
for(int i = 0; i < 49; i++){
Shape line = new Line2D.Double(i,finalpoints[i],i+1,finalpoints[i+1]);
ig2.draw(line);
}
//Export the result to a file
try {
ImageIO.write(bi, "PNG", new File("C://Users/vince/Desktop/heightmap.png"));
} catch (IOException e) {
System.out.println("There was an error writing the image to file");
}
There is two overloaded Line2D.Double constructors: the 1st one takes two Point2D as parameters so if your array contains Point2D objects your code should be :
Shape line = new Line2D.Double(finalpoints[i],finalpoints[i+1]);
the second method Line2D.Double(double x1, double y1, double x2, double y2)
and it takes the points' coordinates, so if you want the second, tour code should be like this:
Shape line = new Line2D.Double(finalpoints[i].getX(), finalpoints[i].getY(), finalpoints[i+1].getX(), finalpoints[i+1].getY());
if your array doesn't contain Point2D objects, just update your post so we can help you.
Setting the foreground color does not fill the background.
Also a Graphics.dispose() is needed.
BufferedImage bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
Graphics2D ig2 = bi.createGraphics();
ig2.setBackground(Color.white);
ig2.setColor(Color.white);
ig2.fillRect(0, 0, 50, 50);
ig2.setColor(Color.red);
// Better use a ig2.drawPolyline (Polygon) so the joints are nicer.
for(int i = 0; i < 49; i++){
Shape line = new Line2D.Double(i,finalpoints[i],i+1,finalpoints[i+1]);
ig2.draw(line);
}
ig2.dispose();
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 have written some code in java to convert a colored image into black and white image and then tried to perform thinning on that gray-scale image. Black and white conversion is done successfully, but image thinning is still not giving correct output. Kindly help me in fixing my problem. My code is as follows:
//colored image to black and white conversion; black and white image to thinned image.
public static void main(String[] args)
{
try
{
//colored image path
BufferedImage colored_image = ImageIO.read(new File("D:\\logo.jpg"));
//getting width and height of image
double image_width = colored_image.getWidth();
double image_height = colored_image.getHeight();
BufferedImage img = colored_image;
//drawing a new image
BufferedImage bimg = new BufferedImage((int)image_width, (int)image_height, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D gg = bimg.createGraphics();
gg.drawImage(img, 0, 0, img.getWidth(null), img.getHeight(null), null);
//saving black and white image onto drive
String temp = "logo in blackAndwhite.jpeg";
File fi = new File("D:\\" + temp);
ImageIO.write(bimg, "jpg", fi);
//thinning by resizing gray scale image to desired eight and width
BufferedImage bimg2 = new BufferedImage((int)image_width, (int)image_height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bimg2.createGraphics();
// Perform your drawing here
g2.setColor(Color.BLACK);
g2.drawLine(0, 0, 200, 200);
//saving thinned image onto drive
String temp2 = "logo thinned.jpeg";
File fi2 = new File("D:\\" + temp2);
ImageIO.write(bimg2, "jpg", fi2);
//g2.dispose();
}
catch (Exception e)
{
System.out.println(e);
}
}
}
Check java.awt.geom.AffineTransform
AffineTransform tx = new AffineTransform();
tx.scale(20, 30);
AffineTransformOp afop = new AffineTransformOp(tx,
AffineTransformOp.TYPE_BILINEAR);
BufferedImage bi = afop.filter(ogininal, null);
Icon icon = new ImageIcon(bi); //here icon will be your thumbnail image