I'm trying to revolve a BufferedImage around its center. Here's my Code:
frame = new JFrame();
GridBagLayout gLayout = new GridBagLayout();
frame.getContentPane().setLayout(gLayout);
lbIm1 = new JLabel(new ImageIcon(img));
lbIm1.setPreferredSize(new Dimension(800, 800));
Timer timer = new Timer(100, e -> {
AffineTransform transform = new AffineTransform();
transform.translate(img.getWidth()/2, img.getHeight()/2);
transform.rotate(Math.toRadians(1), 0, 0);
transform.translate(-img.getWidth()/2, -img.getHeight()/2);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage rotatedImage = op.createCompatibleDestImage(img, img.getColorModel());
op.filter(img, rotatedImage);
lbIm1.setIcon(new ImageIcon(rotatedImage));
img = rotatedImage;
lbIm1.repaint();
});
timer.start();
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 1;
frame.getContentPane().add(lbIm1, c);
frame.pack();
frame.setVisible(true);
The image size is 512 x 512. The problem in this code are:
The rotation is not exactly at center. It would rotate out of the screen.
The imagine became blurry over time.
I have searched online for a very long time and nothing seems to help. Any suggestions would be much appreciated. Thanks.
I would suggestion always rotating form the original image. Keep track of the total rotation.
int angleDegrees = 0;
That way there is less image quality degradation. Normally each time you use a bi-linear interpolation there will be some blurring.
AffineTransform transform = new AffineTransform();
angle += (angle + 1)%360;
transform.rotate(Math.toRad(angle), img.getWidth()/2, img.getHeight()/2);
AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
BufferedImage rotatedImage = op.createCompatibleDestImage(img, ColorModel.getRGBdefault());
op.filter(img, rotatedImage);
lbIm1.setIcon(new ImageIcon(rotatedImage));
lbIm1.repaint();
Note the icon is changing sizes because the destination image is large enough to hold the whole image. That means the center of the icon will appear to move.
If you create the destination image this way.
BufferedImage rotatedImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
Then the destination will be cropped, but stay the same size. That way the center of the image doesn't appear to move.
The transform you're using, translate + rotate + translate back, work with this technique to. The 1 degree rotation causes some rounding error in the size of the image. So I wonder if that is why the image "runs away"
Related
I'm creating a domino game in java. I have the following code that loads, resizes and then display the domino image on the screen:
ImageIcon imageIcon = new ImageIcon("images\\4-4.png");
Image image = imageIcon.getImage();
Image newimg = image.getScaledInstance(60, 120, java.awt.Image.SCALE_SMOOTH);
imageIcon = new ImageIcon(newimg);
JLabel img = new JLabel(imageIcon);
img.setBounds(100, 100, 60, 120);
getContentPane().add(img);
What I want to do is rotate the image either 90 or -90 degrees. I've searched the internet but the examples I've found seems very complicated.
Any idea how I can rotate my image?
Btw, if you think that this is not the correct way to display dominoes in a domino game then please let me know. I'me a java newbie.
Rotating an image is non-trival, even just 90 degrees requires a certain amount of work.
So, based on pretty much every other question about rotating images, I'd start with something like...
public BufferedImage rotate(BufferedImage image, Double degrees) {
// Calculate the new size of the image based on the angle of rotaion
double radians = Math.toRadians(degrees);
double sin = Math.abs(Math.sin(radians));
double cos = Math.abs(Math.cos(radians));
int newWidth = (int) Math.round(image.getWidth() * cos + image.getHeight() * sin);
int newHeight = (int) Math.round(image.getWidth() * sin + image.getHeight() * cos);
// Create a new image
BufferedImage rotate = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotate.createGraphics();
// Calculate the "anchor" point around which the image will be rotated
int x = (newWidth - image.getWidth()) / 2;
int y = (newHeight - image.getHeight()) / 2;
// Transform the origin point around the anchor point
AffineTransform at = new AffineTransform();
at.setToRotation(radians, x + (image.getWidth() / 2), y + (image.getHeight() / 2));
at.translate(x, y);
g2d.setTransform(at);
// Paint the originl image
g2d.drawImage(image, 0, 0, null);
g2d.dispose();
return rotate;
}
While you're only rotate 90 degrees, this takes care of calculating the required size the new image needs in order to be able to paint the rotated image, at any angle.
It then simply makes use of AffineTransform to manipulate the origin point from which painting occurs - get use to this, you will do it a lot.
Then, I load the images, rotate them and display them...
try {
BufferedImage original = ImageIO.read(getClass().getResource("domino.jpg"));
BufferedImage rotated90 = rotate(original, 90.0d);
BufferedImage rotatedMinus90 = rotate(original, -90.0d);
JPanel panel = new JPanel();
panel.add(new JLabel(new ImageIcon(original)));
panel.add(new JLabel(new ImageIcon(rotated90)));
panel.add(new JLabel(new ImageIcon(rotatedMinus90)));
JOptionPane.showMessageDialog(null, panel, null, JOptionPane.PLAIN_MESSAGE, null);
} catch (IOException ex) {
ex.printStackTrace();
}
I prefer to use ImageIO to load images, because it throws an IOException when something goes wrong, rather then failing silently like ImageIcon.
You should also be embedding your resources within your application's context, this makes it easier to load them at runtime. Depending on IDE and how your project is set up, how you do this will change, but in "most" cases, you should be able to add the resource directly to your source directory (preferably in sub directory) and the IDE will make it available for you and package it when you export the project
Solution from: http://www.java2s.com/Code/Java/Advanced-Graphics/RotatingaBufferedImage.htm
AffineTransform tx = new AffineTransform();
tx.rotate(0.5, bufferedImage.getWidth() / 2, bufferedImage.getHeight() / 2);
AffineTransformOp op = new AffineTransformOp(tx,
AffineTransformOp.TYPE_BILINEAR);
bufferedImage = op.filter(bufferedImage, null);
I am trying to set a background image for a ListViewer.
listViewer = new ListViewer(parent);
listViewer.setContentProvider(this);
listViewer.setLabelProvider(this);
listViewer.getList().setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
Image image = new Image(Display.getDefault(), ListPart.class.getResourceAsStream("/icons/blurred.jpg"));
Rectangle imageBounds = image.getBounds();
Rectangle rectangle = listViewer.getList().getBounds();
GC gc = new GC(listViewer.getList());
gc.drawImage(image, imageBounds.x, imageBounds.y,imageBounds.width,imageBounds.height,rectangle.x,rectangle.y,rectangle.width,rectangle.height);
listViewer.getList().setBackgroundImage(image);
I tried to draw the image with GC but it doesn't work. If I leave the image as it is, it doesn't stretch after the List.
Is there an easier way to do this?
I'm trying to detect similar objects in a picture. The purpose of the code is to detect the gold and click on it.
I have tried scanning pixel by pixel but it wasn't efficient and the results weren't satisfying.
I'll add that the game is running on windows mode and classes like robot are working fine. Also the gold might be in different places every time.
As a very quick example I wrote up this using your image:
public class OpenCVTest {
public static void main(String[] args) {
OpenCV.loadLibrary();
Mat m = Highgui.imread("/home/artur/Pictures/test.png", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
LoadImage( m);
Mat res = Mat.zeros(m.size(), m.type());
Imgproc.adaptiveThreshold(m, res, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 15, 20);
LoadImage(res);
Mat cannyRes = Mat.zeros(m.size(), m.type());
Imgproc.Canny(res, cannyRes, 55, 5.2);
LoadImage(cannyRes);
Imgproc.dilate(cannyRes, cannyRes, new Mat(), new Point(-1, -1), 2);
Imgproc.erode(cannyRes, cannyRes, new Mat(), new Point(-1, -1), 2);
LoadImage(cannyRes);
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(cannyRes, contours, new Mat(), Imgproc.RETR_LIST,Imgproc.CHAIN_APPROX_SIMPLE);
System.err.println(contours.size());
contours = contours.stream().filter(s -> s.size().area() > 50 && s.size().area() <= 100).collect(Collectors.toList());
for(MatOfPoint p : contours) {
Size size = p.size();
System.err.println("-- -- --");
System.err.println(size.area());
}
Imgproc.drawContours(cannyRes, contours, 20, new Scalar(233, 223,212));
LoadImage(cannyRes);
}
public static void LoadImage( Mat m) {
Path path = Paths.get("/tmp/", UUID.randomUUID().toString() + ".png");
Highgui.imwrite(path.toString(), m);
JFrame frame = new JFrame("My GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(true);
frame.setLocationRelativeTo(null);
// Inserts the image icon
ImageIcon image = new ImageIcon(path.toString());
frame.setSize(image.getIconWidth() + 10, image.getIconHeight() + 35);
// Draw the Image data into the BufferedImage
JLabel label1 = new JLabel(" ", image, JLabel.CENTER);
frame.getContentPane().add(label1);
frame.validate();
frame.setVisible(true);
}
}
I read the image
I use adaptive threshhold to create a binary image
I use canny to detect the edges in my image
I use dilate/erode to remove background noise
I use the contour finder to find objects in my image
I dismiss any contour that has a arbitrary size
The resulting contours are roughly your yellow spots. This is not very accurate as I didn't invest time playing with the different parameters, but you can fine tune that.
Hope that helps,
Have fun playing. You can see how to set up OpenCV here: Java OpenCV from Maven
I was wondering, how do I merge/join Android canvases. In the code below I have cnv_left which contains a left part of a button. cnv_center contains the center part. And cnv_text contains text.
What I need is to merge them all in cnv_joined , so that
cnv_left would go first.
then cnv_center.
cnv_text would be in center of cnv_center.
and a flipped cnv_left would be the last.
Here's my code so far:
public void drawButt()
{
float buttonScale = 1.0f; /// general button scale ratio
float buttonScaleCnt = 6.0f; /// button's center part stretch ratio
LinearLayout LinLay = (LinearLayout)findViewById(R.id.linearLayout1);
ImageView iv1 = new ImageView(this);
Bitmap bit_left = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas cnv_left = new Canvas(bit_left);
cnv_left.scale(buttonScale,buttonScale);
SVG svg_left = SVGParser.getSVGFromResource(getResources(), R.raw.btleft);
Picture picture_left = svg_left.getPicture();
picture_left.draw(cnv_left);
Bitmap bit_center = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas cnv_center = new Canvas(bit_center);
cnv_center.scale(buttonScaleCnt, buttonScale);
SVG svg_center = SVGParser.getSVGFromResource(getResources(), R.raw.btcnt);
Picture picture_cnt = svg_center.getPicture();
picture_cnt.draw(cnv_center);
Bitmap bit_text = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas cnv_text = new Canvas(bit_text);
cnv_text.scale(buttonScale, buttonScale);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.WHITE); paint.setTextSize(20);
cnv_text.drawText("R", 2, 30, paint);
Bitmap bit_joined = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
Canvas cnv_joined = new Canvas(bit_joined);
/// somehow need to somehow join the above canvases into this cnv_joined...
iv1.setImageBitmap(bit_joined);
iv1.setPadding(5, 5, 5, 5);
LinLay.addView(iv1);
}
Any ideas? Oh, and one more thing, when I create empty bitmaps for my canvas ( Bitmap.createBitmap(100, 100... ), does it matter what size I give them? If yes, where should I get the correct sizes for them?
Thanks!
Size of Bitmaps matter. If you do Picture.draw to canvas smaller than Picture, image will be cropped to size of bitmap.
Call SVG.getBounds to get bounds and put them in Bitmap constructor.
To join Bitmaps together you have to draw bit_left, bit_center and bit_text on cnv_joined using drawBitmap.
Better way will be to draw SVGs and text directly on cnv_joined.
I wanted to display on the JFrame on the program the full screenshot of my screen.
So far using the code below, I was able only to display part of the screen.
The code below is the content of the paint(Graphics g).
How can I make it full screen?
// the screen resolution is 1280 x 1024 while the JPanel size is only 1024 x 768
Dimension resolution = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle rectangle = new Rectangle(resolution);
robot = new Robot();
BufferedImage bufferedImage = robot.createScreenCapture(rectangle);
g.drawImage(bufferedImage.getScaledInstance(bufferedImage.getWidth(), bufferedImage.getHeight(), Image.SCALE_DEFAULT), 0, 0, null);
Maybe using something like this:
//get the screen size
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
BufferedImage image = robot.createScreenCapture (dim);
//other code
//...
I see you have some errors, I don't know if your code even compiles, 'cause references seems not to be declared, but a code similar to this one will caputure a screenshoot of your desktop:
import java.awt.geom.*;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;
import java.io.*;
public class ScreenCapturer
{
public static void main(String[] args)throws Exception
{
Dimension resolution = Toolkit.getDefaultToolkit().getScreenSize();
Rectangle rectangle = new Rectangle(resolution);
Robot robot = new Robot();
BufferedImage bufferedImage = robot.createScreenCapture(rectangle);
Graphics g = bufferedImage.getGraphics();
//g.drawImage(bufferedImage.getScaledInstance(bufferedImage.getWidth(), bufferedImage.getHeight(), Image.SCALE_DEFAULT), 0, 0, null);
File out = new File("image.png");
ImageIO.write(bufferedImage,"png",out);
}
}
I saved to an png image file instead of drawing it on the screen or the frame.
Use java.awt.Toolkit.getDefaultToolkit().getScreenSize() to get the size of the screen: http://www.roseindia.net/java/java-get-example/screen-dimensions.shtml
g.drawImage(bufferedImage.getScaledInstance(bufferedImage.getWidth(), bufferedImage.getHeight(), Image.SCALE_DEFAULT), 0, 0, null);
Simplify your code by only using one statement per line then you might be able to understand the code.
Why are you ue the width and height of the image? How does that scale the image if you specify the full size of the image? I would guess you want:
Image scaled = bufferedImage.getScaledInstance(1024, 768, Image.SCALE_DEFAULT);
Now instead of doing custom painting you can just add your image to a JLabel:
ImageIcon icon = new ImageIcon( scaled );
JLabel label = new JLabel( icon );
frame.add( label );