Can't draw BufferedImage into another BufferedImage with scale - java

Please advise.
I'm trying to draw input BufferedImage into larger output BufferedImage (with scaling). Please, take a look at the following code:
public class Main {
public void print(BufferedImage img, int width, int height) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
System.out.print(img.getRGB(x, y) + " ");
}
System.out.println("");
}
}
public static void main(String[] args) {
Main app = new Main();
// create input image
int inputWidth = 2;
int inputHeight = 2;
BufferedImage inputImg = new BufferedImage(inputWidth, inputHeight, BufferedImage.TYPE_INT_ARGB);
// fill input image
for (int y = 0; y < inputHeight; y++) {
for (int x = 0; x < inputWidth; x++) {
inputImg.setRGB(x, y, y * inputWidth * (1 << 16) + x);
}
}
// print
app.print(inputImg, inputWidth, inputHeight);
// create output image
int outputWidth = 4;
int outputHeight = 4;
BufferedImage outputImg = new BufferedImage(outputWidth, outputHeight, BufferedImage.TYPE_INT_ARGB);
// draw inputImg into outputImg
Graphics2D g = outputImg.createGraphics();
g.drawImage(inputImg, 0, 0, outputImg.getWidth(), outputImg.getHeight(), 0, 0, inputImg.getWidth(), inputImg.getHeight(), null);
// print
app.print(outputImg, outputImg.getWidth(), outputImg.getHeight());
}
}
Execution produces the following output:
0 1
131072 131073
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
Seems like Graphics2D object works, because I'm able to draw, for example, a line calling the drawLine function. So, I think the inputImg is the source of the issue, but I can't figure out what's wrong.
UPDATE:
I've tried to use AffineTransform, but it didn't help, unfortunately.
Graphics2D g = outputImg.createGraphics();
AffineTransform at = new AffineTransform();
at.setToIdentity();
at.scale(2, 2);
g.drawImage(inputImg, at, null);

To me, this seems to be an issue with the color calculation you're using...
When I change...
inputImg.setRGB(x, y, y * inputWidth * (1 << 16) + x);
to...
int rgb = y * inputWidth * (1 << 16) + x;
inputImg.setRGB(x, y, new Color(rgb).getRGB());
I get a result, albeit a black dot. This suggests to me that by default, your calculation is generating a alpha value of 0
This can be born out in the output that they produce:
My method generates
-16777216 -16777215
-16646144 -16646143
Yours generates
0 1
131072 131073
Now, frankly, this is why I don't do this kind of calculation, not when a API is available to do it for me - but I be dumb ;P
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
public class Main {
public void print(BufferedImage img, int width, int height) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
System.out.print(img.getRGB(x, y) + " ");
}
System.out.println("");
}
}
public static void main(String[] args) {
Main app = new Main();
// create input image
int inputWidth = 2;
int inputHeight = 2;
BufferedImage inputImg = new BufferedImage(inputWidth, inputHeight, BufferedImage.TYPE_INT_ARGB);
// fill input image
System.out.println(inputWidth + "x" + inputHeight);
Color color = Color.RED;
for (int y = 0; y < inputHeight; y++) {
for (int x = 0; x < inputWidth; x++) {
int rgb = y * inputWidth * (1 << 16) + x;
inputImg.setRGB(x, y, new Color(rgb).getRGB());
}
}
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(inputImg)));
// print
app.print(inputImg, inputWidth, inputHeight);
// create output image
int outputWidth = 4;
int outputHeight = 4;
BufferedImage outputImg = new BufferedImage(outputWidth, outputHeight, BufferedImage.TYPE_INT_ARGB);
// draw inputImg into outputImg
Graphics2D g = outputImg.createGraphics();
g.drawImage(inputImg, 0, 0, outputImg.getWidth(), outputImg.getHeight(), 0, 0, inputImg.getWidth(), inputImg.getHeight(), null);
g.dispose();
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(outputImg)));
// print
app.print(outputImg, outputImg.getWidth(), outputImg.getHeight());
}
}

Related

Panning a BufferedImage

How can I delete say 50px of the leftmost vertical column of a BufferedImage, and copy that into a new BufferedImage the same size as the original BufferedImage?
class TestCopyImage {
var img: BufferedImage? = null
private val rnd = Random()
fun create(screenWidth: Int, screenHeight: Int) {
img = BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB)
//Grab the graphics object off the image
val graphics = img!!.createGraphics()
//val stroke: Stroke = BasicStroke(1f)
//graphics.setStroke(stroke);
// Fill the image buffer
for (i in 1..screenWidth) {
for (j in 1..screenHeight) {
val r: Int = rnd.nextInt(255)
val g: Int = rnd.nextInt(255)
val b: Int = rnd.nextInt(255)
val randomColor = Color(r, g, b)
graphics.paint = randomColor
graphics.fill(Rectangle(i , j , 1, 1))
}
}
// Get a subimage, deleting 50 pixels of the left-most vertical portion.
img = img!!.getSubimage(50, 0, screenWidth - 50 , screenHeight)
// TODO Now copy that into a new image, same size as the original buffer?
img = BufferedImage(screenWidth, screenHeight, BufferedImage.TYPE_INT_RGB)
}
}
Here's a Java version of what you can do:
int panDist = 50;
BufferedImage subImg = img.getSubimage(panDist, 0, img.getWidth() - panDist, img.getHeight());
BufferedImage newImg = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
for (int x = 0; x < subImg.getWidth(); ++x) {
for (int y = 0; y < subImg.getHeight(); ++y) {
newImg.setRGB(x, y, subImg.getRGB(x, y));
}
}
The subimage isn't really necessary though, you could skip that and just do this instead:
int panDist = 50;
BufferedImage newImg = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
for (int x = panDist; x < img.getWidth(); ++x) {
for (int y = 0; y < img.getHeight(); ++y) {
newImg.setRGB(x - panDist, y, img.getRGB(x, y));
}
}
You could also tweak that slightly to modify the image in-place instead.

Creating/Cropping image base on number of pixels Java

How can i crop an image to a specified number of pixels or create image that the output will be base on number of pixel not rectangular shape.
By using the code below i can only get square or rectangle shape.
BufferedImage out = img.getSubimage(0, 0, 11, 11);
But it only crops it to rectangular shape
import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class raNd{
public static void main(String args[])throws IOException{
//image dimension
int width = 10;
int height = 10;
//create buffered image object img
BufferedImage img = new BufferedImage(width, height,
BufferedImage.TYPE_INT_ARGB);
//file object
File f = null;
//create random image pixel by pixel
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
int a = 255;//(int)(Math.random()*256); //alpha
int r = (int)(Math.random()*256); //red
int g = (int)(Math.random()*256); //green
int b = (int)(Math.random()*256); //blue
int p = (a<<24) | (r<<16) | (g<<8) | b; //pixel
img.setRGB(x, y, p);
}
}
//write image
try{
f = new File("/Users/kingamada/Documents/Java/Test6.png");
BufferedImage out = img.getSubimage(0, 0, 5, 2);
ImageIO.write(out, "png", f);
}catch(IOException e){
System.out.println("Error: " + e);
}
}//main() ends here
}//class ends here
Sample Picture
I want the last white pixels cropped out, so the picture will not be rectangle.
So assuming the number of pixels you need to keep is in variable int pixelsLimit;:
int pixels = 0;
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
int p = 0;
if (pixels < pixelsLimit) {
int a = 255;//(int)(Math.random()*256); //alpha
int r = (int)(Math.random()*256); //red
int g = (int)(Math.random()*256); //green
int b = (int)(Math.random()*256); //blue
p = (a<<24) | (r<<16) | (g<<8) | b; //pixel
}
img.setRGB(x, y, p);
++pixels;
}
}
Java images are rectangular, but people have suggested you can set the pixels you don't want to be transparent.
Ellipse2D clip = new Ellipse2D.Double(0, 0, width, height);
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
if(!clip.contains(x,y)){
img.setRGB(x, y, 0);
}
}
}
This could directly be added to existing code to make your image an ellipse. Another way would be to use a clipping shape and a graphics object. I've replaced your complete write image block.
//write image
try{
f = new File("Test6.png");
Ellipse2D clip = new Ellipse2D.Double(0, 0, width, height);
BufferedImage clipped = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = clipped.getGraphics();
g.setClip(clip); //ellipse from above.
g.drawImage(img, 0, 0, null);
g.dispose();
ImageIO.write(clipped, "png", f);
}catch(IOException e){
System.out.println("Error: " + e);
}
This compiled for me and wrote a tiny circular image.

Converting monochrome image to minimum number of 2d shapes

Basically, what I need to do is take a 2d array of bitflags and produce a list of 2d rectangles to fill the entire area with the minimum number of total shapes required to perfectly fill the space. I am doing this to convert a 2d top-down monochrome of a map into 2d rectangle shapes which perfectly represent the passed in image which will be used to generate a platform in a 3d world. I need to minimize the total number of shapes used, because each shape will represent a separate object, and flooding it with 1 unit sized squares for each pixel would be highly inefficient for that engine.
So far I have read in the image, processed it, and filled a two dimensional array of booleans which tells me if the pixel should be filled or unfilled, but I am unsure of the most efficient approach of continuing.
Here is what I have so far, as reference, if you aren't following:
public static void main(String[] args) {
File file = new File(args[0]);
BufferedImage bi = null;
try {
bi = ImageIO.read(file);
} catch (IOException ex) {
Logger.global.log(Level.SEVERE, null, ex);
}
if (bi != null) {
int[] rgb = bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), new int[bi.getWidth() * bi.getHeight()], 0, bi.getWidth());
Origin origin = new Origin(bi.getWidth() / 2, bi.getHeight() / 2);
boolean[][] flags = new boolean[bi.getWidth()][bi.getHeight()];
for (int y = 0; y < bi.getHeight(); y++) {
for (int x = 0; x < bi.getWidth(); x++) {
int index = y * bi.getWidth() + x;
int color = rgb[index];
int type = color == Color.WHITE.getRGB() ? 1 : (color == Color.RED.getRGB() ? 2 : 0);
if (type == 2) {
origin = new Origin(x, y);
}
flags[x][y] = type != 1;
}
}
List<Rectangle> list = new ArrayList();
//Fill list with rectangles
}
}
White represents no land. Black or Red represents land. The check for the red pixel marks the origin position of map, which was just for convenience and the rectangles will be offset by the origin position if it is found.
Edit: The processing script does not need to be fast, the produced list of rectangles will be dumped and that will be what will be imported and used later, so the processing of the image does not need to be particularly optimized, it doesn't make a difference.
I also just realized that expecting a 'perfect' solution is expecting too much, since this would qualify as a 'knapsack problem' of the multidimensionally constrained variety, if I am expecting exactly the fewest number of rectangles, so simply an algorithm that produces a minimal number of rectangles will suffice.
Here is a reference image for completion:
Edit 2: It doesn't look like this is such an easy thing to answer given no feedback yet, but I have started making progress, but I am sure I am missing something that would vastly reduce the number of rectangles. Here is the updated progress:
static int mapWidth;
static int mapHeight;
public static void main(String[] args) {
File file = new File(args[0]);
BufferedImage bi = null;
System.out.println("Reading image...");
try {
bi = ImageIO.read(file);
} catch (IOException ex) {
Logger.global.log(Level.SEVERE, null, ex);
}
if (bi != null) {
System.out.println("Complete!");
System.out.println("Interpreting image...");
mapWidth = bi.getWidth();
mapHeight = bi.getHeight();;
int[] rgb = bi.getRGB(0, 0, mapWidth, mapHeight, new int[mapWidth * mapHeight], 0, mapWidth);
Origin origin = new Origin(mapWidth / 2, mapHeight / 2);
boolean[][] flags = new boolean[mapWidth][mapHeight];
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
int index = y * mapWidth + x;
int color = rgb[index];
int type = color == Color.WHITE.getRGB() ? 1 : (color == Color.RED.getRGB() ? 2 : 0);
if (type == 2) {
origin = new Origin(x, y);
}
flags[x][y] = type != 1;
}
}
System.out.println("Complete!");
System.out.println("Processing...");
//Get Rectangles to fill space...
List<Rectangle> rectangles = getRectangles(flags, origin);
System.out.println("Complete!");
float rectangleCount = rectangles.size();
float totalCount = mapHeight * mapWidth;
System.out.println("Total units: " + (int)totalCount);
System.out.println("Total rectangles: " + (int)rectangleCount);
System.out.println("Rectangle reduction factor: " + ((1 - rectangleCount / totalCount) * 100.0) + "%");
System.out.println("Dumping data...");
try {
file = new File(file.getParentFile(), file.getName() + "_Rectangle_Data.txt");
if(file.exists()){
file.delete();
}
file.createNewFile();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
for(Rectangle rect: rectangles){
bw.write(rect.x + "," + rect.y + "," + rect.width + ","+ rect.height + "\n");
}
bw.flush();
bw.close();
} catch (Exception ex) {
Logger.global.log(Level.SEVERE, null, ex);
}
System.out.println("Complete!");
}else{
System.out.println("Error!");
}
}
public static void clearRange(boolean[][] flags, int xOff, int yOff, int width, int height) {
for (int y = yOff; y < yOff + height; y++) {
for (int x = xOff; x < xOff + width; x++) {
flags[x][y] = false;
}
}
}
public static boolean checkIfFilled(boolean[][] flags, int xOff, int yOff, int width, int height) {
for (int y = yOff; y < yOff + height; y++) {
for (int x = xOff; x < xOff + width; x++) {
if (!flags[x][y]) {
return false;
}
}
}
return true;
}
public static List<Rectangle> getRectangles(boolean[][] flags, Origin origin) {
List<Rectangle> rectangles = new ArrayList();
for (int y = 0; y < mapHeight; y++) {
for (int x = 0; x < mapWidth; x++) {
if (flags[x][y]) {
int maxWidth = 1;
int maxHeight = 1;
Loop:
//The search size limited to 400x400 so it will complete some time this century.
for (int w = Math.min(400, mapWidth - x); w > 1; w--) {
for (int h = Math.min(400, mapHeight - y); h > 1; h--) {
if (w * h > maxWidth * maxHeight) {
if (checkIfFilled(flags, x, y, w, h)) {
maxWidth = w;
maxHeight = h;
break Loop;
}
}
}
}
//Search also in the opposite direction
Loop:
for (int h = Math.min(400, mapHeight - y); h > 1; h--) {
for (int w = Math.min(400, mapWidth - x); w > 1; w--) {
if (w * h > maxWidth * maxHeight) {
if (checkIfFilled(flags, x, y, w, h)) {
maxWidth = w;
maxHeight = h;
break Loop;
}
}
}
}
rectangles.add(new Rectangle(x - origin.x, y - origin.y, maxWidth, maxHeight));
clearRange(flags, x, y, maxWidth, maxHeight);
}
}
}
return rectangles;
}
My current code's search for larger rectangles is limited to 400x400 to speed up testing, and outputs 17,979 rectangles, which is a 99.9058% total reduction of rectangles if I treated each pixel as a 1x1 square(19,095,720 pixels). So far so good.

Image processing using java

I am writing a java code that divides an image into chunks and rotate to some degree and combine the chunks to become one final image. Then use same code to divide the image into chunks and rotate opposite. I expect to get the same image as the original but I get an image with black line separated between them. For example an image is divided into 8 rows and 8 columns and conduct rotation. I have googled it and come up with the following code:
public static BufferedImage Didvide( BufferedImage image , int Bz ,double angle ){
int rows = Bz;
int cols = Bz;
int chunks = rows * cols;
int chunkWidth = image.getWidth() / cols;
int chunkHeight = image.getHeight() / rows;
int count = 0;
BufferedImage imgs[] = new BufferedImage[chunks];
for (int x = 0; x < rows; x++) {
for (int y = 0; y < cols; y++) {
imgs[count] = new BufferedImage(chunkWidth, chunkHeight,
image.getType());
// draws image chunk
Graphics2D gr = imgs[count++].createGraphics();
gr.drawImage(image, 0, 0, chunkWidth, chunkHeight, chunkWidth
* y, chunkHeight * x, chunkWidth * y + chunkWidth,
chunkHeight * x + chunkHeight, null);
gr.dispose();
}
}
BufferedImage[] Rimgs = new BufferedImage[imgs.length];
for (int i = 0; i < imgs.length; i++) {
Rimgs[i] = rotate(imgs[i], angle);
}
chunkWidth = Rimgs[0].getWidth();
chunkHeight = Rimgs[0].getHeight();
// Initializing the final image
BufferedImage finalImg = new BufferedImage(chunkWidth * cols,
chunkHeight * rows, BufferedImage.TYPE_3BYTE_BGR);
int num = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
finalImg.createGraphics().drawImage(Rimgs[num], chunkWidth * j,
chunkHeight * i, null);
num++;
} } return finalImg; }
public static BufferedImage rotate(BufferedImage image, double angle) {
double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle));
int w = image.getWidth(), h = image.getHeight();
int neww = (int) Math.floor(w * cos + h * sin), newh = (int) Math
.floor(h * cos + w * sin);
GraphicsConfiguration gc = getDefaultConfiguration();
BufferedImage result = gc.createCompatibleImage(neww, newh,
Transparency.OPAQUE);
Graphics2D g = result.createGraphics();
g.translate((neww - w) / 2, (newh - h) / 2);
g.rotate(angle, w / 2, h / 2);
g.drawRenderedImage(image, null);
g.dispose();
return result;
}
The problem I face after dividing an image of baboo gray-scale 298X298 pixel into 8 col and 8 row, the resulting image has black lines separating columns. However when I divide the image into 12 or 4 it works fine. Can you please let me know where I should consider.
Seems I can not post image.
When I divide and rotate the image into 8 rows and 8 columns of an image with 298X298, I get a result of 296X296 pixel. How can I fix this. So the size of before dividing and rotating is same as after.
Thanks in advance for your help.

How to make Histogram Normalize and Equalize in java using JAI library?

I m making App in java using Swing component and JAI library. I make histogram of black and white or gray scale image.Is this method of making histogram correct? iif it is correct then how can i do normalization and Equalization of histogram in my App in java using JAI library?my code is below. in my code i make BufferedImage object and then make and plot histogram of that image .
enter code here
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.media.jai.JAI;
import javax.media.jai.PlanarImage;
import javax.swing.*;
public class FinalHistogram extends JPanel {
static int[] bins = new int[256];
static int[] newBins = new int[256];
static int x1 = 0, y1 = 0;
static PlanarImage image = JAI.create("fileload", "alp_finger.tiff");
static BufferedImage bi = image.getAsBufferedImage();
FinalHistogram(int[] pbins) {
for (int i = 0; i < 256; i++) {
bins[i] = pbins[i];
newBins[i] = 0;
}
repaint();
}
#Override
protected void paintComponent(Graphics g) {
for (int i = 0; i < 256; i++) {
g.drawLine(150 + i, 300, 150 + i, 300 - (bins[i] / 300));
if (i == 0 || i == 255) {
String sr = new Integer((i)).toString();
g.drawString(sr, 150 + i, 305);
}
System.out.println("bin[" + i + "]===" + bins[i]);
}
}
public static void main(String[] args) throws IOException {
int[] sbins = new int[256];
int pixel = 0;
int k = 0;
for (int x = 0; x < bi.getWidth(); x++) {
for (int y = 0; y < bi.getHeight(); y++) {
pixel = bi.getRaster().getSample(x, y, 0);
k = (int) (pixel / 256);
sbins[k]++;
//pixel = bi.getRGB(x, y) & 0x000000ff;
//k=pixel;
//int[] pixels = m_image.getRGB(0, 0, m_image.getWidth(), m_image.getHeight(), null, 0, m_image.getWidth());
//short currentValue = 0;
//int red,green,blue;
//for(int i = 0; i<pixels.length; i++){
//red = (pixels[i] >> 16) & 0x000000FF;
//green = (pixels[i] >>8 ) & 0x000000FF;
//blue = pixels[i] & 0x000000FF;
//currentValue = (short)((red + green + blue) / 3); //Current value gives the average //Disregard the alpha
//assert(currentValue >= 0 && currentValue <= 255); //Something is awfully wrong if this goes off...
//m_histogramArray[currentValue] += 1; //Increment the specific value of the array
//}
}
}
JTabbedPane jtp = new JTabbedPane();
jtp.addTab("Histogram", new JScrollPane(new FinalHistogram(sbins)));
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.add(new JScrollPane(jtp));
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

Categories

Resources