Cropping BufferedImage For Use in Xuggle encodeVideo - java

I have an application to capture video of the screen and save to a file. I give the user the ability to pick between 480, 720, and "Full Screen" video sizes. A 480 will record in a small box on the screen, 720 will record in a larger box, and of course, "Full Screen" will record in an even larger box. However, this full screen box is NOT the actual screen resolution. It is the app window size, which happens to be around 1700x800. The Video Tool works perfectly for the 480 and 720 options, and will also work if "Full Screen" is overwridden to be the entire screen of 1920x1080.
My question: Are only certain sizes allowed? Does it have to fit a certain aspect ratio, or be an "acceptable" resolution? My code, below, is modified from the xuggle CaptureScreenToFile.java file (the location of the problem is noted by comments):
public void run() {
try {
String parent = "Videos";
String outFile = parent + "example" + ".mp4";
file = new File(outFile);
// This is the robot for taking a snapshot of the screen. It's part of Java AWT
final Robot robot = new Robot();
final Rectangle customResolution = where; //defined resolution (custom record size - in this case, 1696x813)
final Toolkit toolkit = Toolkit.getDefaultToolkit();
final Rectangle fullResolution = new Rectangle(toolkit.getScreenSize()); //full resolution (1920x1080)
// First, let's make a IMediaWriter to write the file.
final IMediaWriter writer = ToolFactory.makeWriter(outFile);
writer.setForceInterleave(false);
// We tell it we're going to add one video stream, with id 0,
// at position 0, and that it will have a fixed frame rate of
// FRAME_RATE.
writer.addVideoStream(0, 0, FRAME_RATE, customResolution.width, customResolution.height); //if I use fullResolution, it works just fine - but captures more of the screen than I want.
// Now, we're going to loop
long startTime = System.nanoTime();
while (recording) {
// take the screen shot
BufferedImage screen = robot.createScreenCapture(fullResolution); //tried capturing using customResolution, but did not work. Instead, this captures full screen, then tries to trim it below (also does not work).
// convert to the right image type
BufferedImage bgrScreen = convertToType(screen, BufferedImage.TYPE_3BYTE_BGR); //Do I need to convert after trimming?
BufferedImage trimmedScreen = bgrScreen.getSubimage((int)customResolution.getX(), (int)customResolution.getY(), (int)customResolution.getWidth(), (int)customResolution.getHeight());
// encode the image
try{
//~~~~Problem is this line of code!~~~~ Error noted below.
writer.encodeVideo(0, trimmedScreen, System.nanoTime() - startTime, TimeUnit.NANOSECONDS); //tried using trimmedScreen and bgrScreen
} catch (Exception e) {
e.printStackTrace();
}
// sleep for framerate milliseconds
Thread.sleep((long) (1000 / FRAME_RATE.getDouble()));
}
// Finally we tell the writer to close and write the trailer if
// needed
writer.close();
} catch (Throwable e) {
System.err.println("an error occurred: " + e.getMessage());
}
}
public static BufferedImage convertToType(BufferedImage sourceImage, int targetType) {
BufferedImage image;
// if the source image is already the target type, return the source image
if (sourceImage.getType() == targetType)
image = sourceImage;
// otherwise create a new image of the target type and draw the new image
else {
image = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), targetType);
image.getGraphics().drawImage(sourceImage, 0, 0, null);
}
return image;
}
Error:
java.lang.RuntimeException: could not open stream com.xuggle.xuggler.IStream#2834912[index:0;id:0;streamcoder:com.xuggle.xuggler.IStreamCoder#2992432[codec=com.xuggle.xuggler.ICodec#2930320[type=CODEC_TYPE_VIDEO;id=CODEC_ID_H264;name=libx264;];time base=1/50;frame rate=0/0;pixel type=YUV420P;width=1696;height=813;];framerate:0/0;timebase:1/90000;direction:OUTBOUND;]: Operation not permitted
Note: The file is successfully created, but has size of zero, and cannot be opened by Windows Media Player, with the following error text:
Windows Media Player cannot play the file. The Player might not support the file type or might not support the codec that was used to compress the file.
Sorry for the wordy question. I'm interested in learning WHAT and WHY, not just a solution. So if anyone can explain why it isn't working, or point me towards material to help, I'd appreciate it. Thanks!

Try to have the dimension even numbers 1696x812

Related

Java Image Processing: Why is the output image smaller in storage than the input image?

I'm learning Java Image Processing. Here is my code:
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
public class LoadImage {
public static void main(String[] args) {
int width = 1280;
int height = 720;
BufferedImage image = null;
// READ IMAGE
try {
File input_image = new File("E:\\SELF-TAUGHT LEARNING\\39. Image Processing with Java\\test-image.jpg");
image = new BufferedImage (width,height,BufferedImage.TYPE_INT_ARGB);
image = ImageIO.read(input_image);
System.out.println("Read successfully");
}
catch (IOException e) {
System.out.println("Error: " + e);
}
// WRITE IMAGE
try {
File output_image = new File("E:\\SELF-TAUGHT LEARNING\\39. Image Processing with Java\\test-image-output.jpg");
ImageIO.write(image, "jpg", output_image);
System.out.println("Writing successfully");
}
catch (IOException e) {
System.out.println("Error: "+ e);
}
}
}
So the input image is around 300kb. But the output image is only 48kb. Why? Thank you
If the image is the same resolution and quality after saving the new version, it's likely just a difference of metadata and formatting. It's also possible, however, that you are getting a lower quality image - if the original image had a high enough quality setting, it may not be noticeable when Java saves it at a lower quality.
Some things to check:
do the before and after images look different?
is the after image smaller in size?
I'm not very knowledgeable about Java, and don't know a lot about their image processing, but I would imagine there are methods for setting image size, resolution, and quality. I do, however, work with images a lot, and I know there are a lot of optimizations (especially in JPEG images) that you can do to reduce file size without affecting visual quality noticeably.

BufferedImage on SWT Canvas widget?

Ok, I'm stubborn, I admit it. But...
I want to use canvas(or even label, doesn't really matter for me now) to show image in app window. But SWT accepts only and ONLY Image and after almost 2 hours now, trying few solutions, with http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/ConvertbetweenSWTImageandAWTBufferedImage.htm on top of that and no luck. Is there any way of doing it without using utility like this? Trying to start with OpenCV already, so any way of converting Mat to Image would be also helpful.
Is there anything I can put here to help?
I'm using method below to get BufferedImage
public static BufferedImage mat2Img(Mat in)
{
BufferedImage out;
byte[] data = new byte[640 * 480 * (int)in.elemSize()];
int type;
in.get(0, 0, data);
if(in.channels() == 1)
type = BufferedImage.TYPE_BYTE_GRAY;
else
type = BufferedImage.TYPE_3BYTE_BGR;
out = new BufferedImage(640, 480, type);
out.getRaster().setDataElements(0, 0, 640, 480, data);
return out;
}
and method from link above to convert it to Image. Still, it doesn't work and gives me an exception with(for me) unknown source. Code:
VideoCapture camera=new VideoCapture(0);
frame=new Mat(); //some are static
camera.read(frame);
bim=mat2Img(frame); //it's buffered image
im=new Image(display, AWT_SWT_converter.convertToSWT(bim)); //gives error here
and error:
Exception in thread "main" java.lang.IllegalArgumentException: Argument cannot be null
at org.eclipse.swt.SWT.error(SWT.java:4422)
at org.eclipse.swt.SWT.error(SWT.java:4356)
at org.eclipse.swt.SWT.error(SWT.java:4327)
at org.eclipse.swt.graphics.Image.init(Image.java:1943)
at org.eclipse.swt.graphics.Image.<init>(Image.java:453)
at com.ilmash.opencv.Main.main(Main.java:48)

JLabel Icon not showing after change

I tried looking up this question, but most of the answers are that the file path is wrong, but that's most likely not the case. The file works the 1st time I use it.
I am making a battleships game, and use JLabels to show ships on map. I want to make a button to rotate ship from horizontal to vertical but it's icon disappears when I try to change it.
When I run this constructor code:
public Ship(int size, String direction, boolean active, Client c,
ClientGUI cg) {
this.c = c;
this.cg = cg;
health = size;
this.active = active;
this.direction = direction;
file = "img/" + Integer.toString(health) + direction + ".png"; // String
try {
System.out.println(file);
tx = ImageIO.read(new File(file)); // BufferedImage
} catch (IOException e) {
e.printStackTrace();
}
texture = new JLabel(new ImageIcon(tx));
if (direction.equals("v"))
texture.setBounds(0, 0, 40, 40 * size);
else
texture.setBounds(0, 0, 40 * size, 40);
texture.setVisible(true);
}
everything works and I can see the image.
But then I try to rotate it, using pretty much the same code:
void rotate() {
if (direction.equals("h")) {
direction = "v";
file = "img/" + Integer.toString(health) + direction + ".png";
try {
System.out.println(file);
tx = ImageIO.read(new File(file));
} catch (IOException e) {
e.printStackTrace();
}
texture.setBounds(0,0,40, 40 * size);
texture.setIcon(new ImageIcon(tx));
} else {
direction = "h";
file = "img/" + Integer.toString(health) + direction + ".png";
try {
System.out.println(file);
tx = ImageIO.read(new File(file));
} catch (IOException e) {
e.printStackTrace();
}
texture.setIcon(new ImageIcon(tx));
texture.setBounds(0,0,40 * size, 40);
}
cg.repaint(); // not sure if I need to do this
}
and it disappears...
I tried placing two ships, one is rotated, it's just missing the JLabel or the icon on JLabel.
If you update the JLabel texture by calling a method which changes it's state, it may or may not be updated immediately unless you call texture.repaint() or texture.paintAll(texture.getGraphics()), or some similar method.
Also, I would look into using a LayoutManager for whatever upper level component you are using to hold your grid of JLabels. If you use a GridLayout of your game board and either:
set the JLabel's preferred size with texture.setPreferredSize(Dimension) and call frame.pack() once when setting up your game; or
set the JLabel's size with label.setSize(Dimension) once and don't pack your JFrame
You will only need to set the size of the JLabel once, not every time you set a new ImageIcon to the label. Because, Ideally your game shouldn't be doing any extra work that it doesn't have to so it performs faster.
I would also recommend maintaining every possible ImageIcon as static fields in your class, rather than accessing them from the file every time. That way, you read them once from a static initializing method, which then reach ship can directly access when changing the direction.
I want to make a button to rotate ship from horizontal to vertical
You can use the Rotated Icon class to do the rotation for you.

java ImageIO resolution

I've been searching for some solutions from the internet yet I still haven't found an answer to my problem.
I've been working or doing a program that would get an image file from my PC then will be edited using Java Graphics to add some text/object/etc. After that, Java ImageIO will save the newly modified image.
So far, I was able to do it nicely but I got a problem about the size of the image. The original image and the modified image didn't have the same size.
The original is a 2x3inches-image while the modified one which supposedly have 2x3inches too sadly got 8x14inches. So, it has gone BIGGER than the original one.
What is the solution/code that would give me an output of 2x3inches-image which will still have a 'nice quality'?
UPDATE:
So, here's the code I used.
public Picture(String filename) {
try {
File file = new File("originalpic.jpg");
image = ImageIO.read(file);
width = image.getWidth();
}
catch (IOException e) {
throw new RuntimeException("Could not open file: " + filename);
}
}
private void write(int id) {
try {
ImageIO.write(image, "jpg", new File("newpic.jpg"));
} catch (IOException e) {
e.printStackTrace();
}
}
2nd UPDATE:
I now know what's the problem of the new image. As I check it from Photoshop, It has a different image resolution compared to the original one. The original has a 300 pixels/inch while the new image has a 72 pixels/inch resolution.
How will I be able to change the resolution using Java?

Java BufferedImage alternatives

I am trying to implement a simple class that will allow a user to crop an image to be used for their profile picture. This is a java web application.
I have done some searching and found that java.awt has a BufferedImage class, and this appears (at first glance) to be perfect for what I need. However, it seems that there is a bug in this (or perhaps java, as I have seen suggested) that means that the cropping does not always work correctly.
Here is the code I am using to try to crop my image:
BufferedImage profileImage = getProfileImage(form, modelMap);
if (profileImage != null) {
BufferedImage croppedImage = profileImage
.getSubimage(form.getStartX(), form.getStartY(), form.getWidth(), form.getHeight());
System.err.println(form.getStartX());
System.err.println(form.getStartY());
File finalProfileImage = new File(form.getProfileImage());
try {
String imageType = getImageType(form.getProfileImage());
ImageIO.write(croppedImage, imageType, finalProfileImage);
}
catch (Exception e) {
logger.error("Unable to write cropped image", e);
}
}
return modelAndView;
}
protected BufferedImage getProfileImage(CropImageForm form, Map<String, Object> modelMap) {
String profileImageFileName = form.getProfileImage();
if (validImage(profileImageFileName) && imageExists(profileImageFileName)) {
BufferedImage image = null;
try {
image = getCroppableImage(form, ImageIO.read(new File(profileImageFileName)), modelMap);
}
catch (IOException e) {
logger.error("Unable to crop image, could not read profile image: [" + profileImageFileName + "]");
modelMap.put("errorMessage", "Unable to crop image. Please try again");
return null;
}
return image;
}
modelMap.put("errorMessage", "Unable to crop image. Please try again.");
return null;
}
private boolean imageExists(String profileImageFileName) {
return new File(profileImageFileName).exists();
}
private BufferedImage getCroppableImage(CropImageForm form, BufferedImage image, Map<String, Object> modelMap) {
int cropHeight = form.getHeight();
int cropWidth = form.getWidth();
if (cropHeight <= image.getHeight() && cropWidth <= image.getWidth()) {
return image;
}
modelMap.put("errorMessage", "Unable to crop image. Crop size larger than image.");
return null;
}
private boolean validImage(String profileImageFileName) {
String extension = getImageType(profileImageFileName);
return (extension.equals("jpg") || extension.equals("gif") || extension.equals("png"));
}
private String getImageType(String profileImageFileName) {
int indexOfSeparator = profileImageFileName.lastIndexOf(".");
return profileImageFileName.substring(indexOfSeparator + 1);
}
The form referred to in this code snippet is a simple POJO which contains integer values of the upper left corner to start cropping (startX and startY) and the width and height to make the new image.
What I end up with, however, is a cropped image that always starts at 0,0 rather than the startX and startY position. I have inspected the code to make sure the proper values are being passed in to the getSubimage method, and they appear to be.
Are there simple alternatives to using BufferedImage for cropping an image. I have taken a brief look at JAI. I would rather add a jar to my application than update the jdk installed on all of the production boxes, as well as any development/testing servers and local workstations.
My criteria for selecting an alternative are:
1) simple to use to crop an image as this is all I will be using it for
2) if not built into java or spring, the jar should be small and easily deployable in a web-app
Any suggestions?
Note: The comment above that there is an issue with bufferedImage or Java was something I saw in this posting: Guidance on the BufferedImage.getSubimage(int x, int y, int w, int h) method?
I have used getSubimage() numerous times before without any problems. Have you added a System.out.println(form.getStartX() + " " + form.getStartY()) before that call to make sure they're not both 0?
Also, are you at least getting an image that is form.getWidth() x form.getHeight()?
Do make sure you are not modifying/disposing profileImage in any way since the returned BufferedImage shares the same data array as the parent.
The best way is to just simply draw it across if you want a completely new and independent BufferedImage:
BufferedImage croppedImage = new BufferedImage(form.getWidth(),form.getHeight(),BufferedImage.TYPE_INT_ARGB);
Graphics g = croppedImage.getGraphics();
g.drawImage(profileImage,0,0,form.getWidth(),form.getHeight(),form.getStartX(),form.getStartY(),form.getWidth(),form.getHeight(),null);
g.dispose();
You can do it in this manner as well (code is not 100% tested as I adopted for example from an existing app i did):
import javax.imageio.*;
import java.awt.image.*;
import java.awt.geom.*;
...
BufferedImage img = ImageIO.read(imageStream);
...
/*
* w = image width, h = image height, l = crop left, t = crop top
*/
ColorModel dstCM = img.getColorModel();
BufferedImage dst = new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(w, h), dstCM.isAlphaPremultiplied(), null);
Graphics2D g = dst.createGraphics();
g.drawRenderedImage(img, AffineTransform.getTranslateInstance(-l,-t));
g.dispose();
java.io.File outputfile = new java.io.File(sessionScope.get('absolutePath') + java.io.File.separator + sessionScope.get('lastUpload'));
ImageIO.write(dst, 'png', outputfile);
Thanks for all who replied. It turns out that the problem was not in the cropping code at all.
When I displayed the image to be cropped, I resized it to fit into my layout nicely, then used a javascript cropping tool to figure out the coordinates to crop.
Since I had resized my image, but didn't take the resizing into account when I was determining the cropping coordinates, I ended up with coordinates that appeared to coincide with the top left corner.
I have changed the display to no longer resize the image, and now cropping is working beautifully.

Categories