I have bunch of photos sorted in a folder so that there is always one photo of type A and one photo of type B immediately after that.
Unfortunately, some of them needed to be rotated and I did so using standard Windows file explorer.
What I need is to combine each pair of photos A and B into new single photo so that the first source photo is displayed above the second. Both photos have same width.
Here is the code :
File first = ...;
File second = ...;
BufferedImage A = ImageIO.read(first);
BufferedImage B = ImageIO.read(second);
int resultHeight = A.getHeight() + B.getHeight();
int resultWidth = A.getWidth();
BufferedImage combined = new BufferedImage(resultWidth, resultHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = combined.getGraphics();
g.drawImage(A, 0, 0, null);
g.drawImage(B, 0, A.getHeight(), null);
g.dispose();
ImageIO.write(combined, "PNG", new File(destDirectory, destName));
Unfortunately, although all pictures seem to have same orientation when viewing with the Windows app, they are rotated differently in the result photos. I know there is some kind of flag that seems to be ignored by the BufferedImage.
How can I detect that flag and eventually rotate the photo as needed before combining? Thanks!
I suggest to use the MetadataExtractor to get the orientation of the images. See this question.
Related
This question already has an answer here:
Load large picture from file and add watermark
(1 answer)
Closed 8 years ago.
I have a large jpeg file say 10000 x 150000 px. I want to add a small logo to the bottom of the image without re sizing.
I am able to do this If i down sample the original image and draw the logo using canvas.But when i finally save it to file, the image original size will be reduced as I am sampling it.
If i load the original image into bitmap without down sampling, it exceeds the VM.
Below code work for me :-
public static Bitmap mark(Bitmap src, String watermark, Point location, Color color, int alpha, int size, boolean underline) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
paint.setColor(color);
paint.setAlpha(alpha);
paint.setTextSize(size);
paint.setAntiAlias(true);
paint.setUnderlineText(underline);
canvas.drawText(watermark, location.x, location.y, paint);
return result;
}
For large image editing you'll need to use native tools like imagemagick. Because there seem to be a lack of advanced image processing libraries in android supported Java.
If you can compile Composite tool's binaries for android. Then you can use them with --limit option to work with limited memory.
Also, you can try OpenCV as an alternative.
You can use BitmapRegionDecoder when deal with large image file. From the official document.
BitmapRegionDecoder can be used to decode a rectangle region from an image. BitmapRegionDecoder is particularly useful when an original image is large and you only need parts of the image.
To create a BitmapRegionDecoder, call newInstance(...). Given a BitmapRegionDecoder, users can call decodeRegion() repeatedly to get a decoded Bitmap of the specified region.
Just decode the part of your image that you need to add watermark, then use Canva to draw text on it.
try {
BitmapRegionDecoder regionDecoder = BitmapRegionDecoder.newInstance("/sdcard/test.png", true);
Bitmap bitmap = regionDecoder.decodeRegion(rect, options);
} catch (IOException e) {
e.printStackTrace();
}
I've been looking for a solution for the last several days.
I've seen an example of composite images with Java Advanced Imaging. But that seems to be restricted by the smallest width and height of either image files. So it outputs a file with the height and width of the header file.
Preferably, I'd like to have the header not covering any part of the body image. But it's not a requirement. Sometimes the body image's width is smaller than the header and that's fine as the main content of the header file will be in the middle.
Using JDK 1.6.0_41, I need to take the first two images:
And have the result be:
Whether it is using Java or Javscript is fine. The entire process is as follows:
I take a canvas object of a map using OpenLayers, then use a POST to send it to a Java Servlet to be processed and stored. Then later retrieved the image if the user desires.
The long blue header needs to be at the top of an image or just above it. The header image will have content from the user that created it, etc. That I can do. But manipulating multiple images is not something I am familiar with.
In Java, you can do this:
public BufferedImage prependImage(BufferedImage image1, BufferedImage image2) {
Dimension d1 = new Dimension(image1.getWidth(null),
image1.getHeight(null));
Dimension d2 = new Dimension(image2.getWidth(null),
image2.getHeight(null));
Dimension dt = new Dimension(d1.width, d1.height + d2.height);
BufferedImage image = new BufferedImage(dt.width, dt.height,
BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
int x = 0;
int y = 0;
g.drawImage(image1, x, y, d1.width, d1.height, null);
y += d1.height;
g.drawImage(image2, x, y, d2.width, d2.height, null);
g.dispose();
return image;
}
I'm saving a very large PNG (25 MB or so) with Java. The problem is that while it's being generated, it's using 3+ gigabytes of memory, which is not ideal since it severely slows down systems with low memory.
The code I'm working with needs to combine a set of tiled images into a single image; in other words, I have nine images (PNG):
A1 A2 A3
B1 B2 B3
C1 C2 C3
which need to be combined into a single image.
The code I'm using is this:
image = new BufferedImage(width, height, height, BufferedImage.TYPE_INT_ARGB_PRE);
g2d = image.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
// draw the 9 images on here at their proper positions...
// save image
g2d.dispose();
File file = getOutputFile();
ImageIO.write(image, "png", file);
Is there a way to make and save an image without having the entire image in memory?
Edit:
To draw the images, I'm doing this in a loop:
BufferedImage tile = ImageIO.read(new File("file.png"));
g2d.drawImage(tile, x, y, w, h);
This is being repeated many times (it's usually about 25x25, but sometimes more), so if there is even a small memory leak here, that could be causing the problem.
You can also take a look at this PNGJ library (disclaimer: I coded it), it allows to save a PNG image line by line.
ImageIO.write(image, "png", file); is internally using com.sun.imageio.plugins.png.PNGImageWriter. That method and that writer expect image to be a rendered image but PNG writting is done by 'bands' so you can make a subclass of RenderedImage that generates the requested bands of the composed large image as the writer ask for that bands to the image.
From PNGImageWriter class:
private void encodePass(ImageOutputStream os,
RenderedImage image,
int xOffset, int yOffset,
int xSkip, int ySkip) throws IOException {
// (...)
for (int row = minY + yOffset; row < minY + height; row += ySkip) {
Rectangle rect = new Rectangle(minX, row, width, 1); // <--- *1
Raster ras = image.getData(rect); // <--- *2
*2 I think this is the only place where the writer reads pixels from you image. You should make a getData(rect) method that computes that rect joining 3 bands from 3 images into one.
*1 As you see it reads bands with a height of 1 pixel.
If the things are as I think you should only need to compose 3 images at a time. There would be no need for the other 6 to be in memory.
I know it is not an easy solution but it might help you if you don't find anything easier.
Would using an external tool be an option? I remember using ImageMagick for similar purpose, you would need to save your smaller images first.
I have working on an application which captures screen shots and create video from captured images. But the problem is that when video is generated, colours in generated video is very pinkish. I think this is because I am manipulating captured images to show cursor using BufferedImage.TYPE_3BYTE_BGR type. Could someone tell me how to resolve this issue, I want to have the colour of video same as actual colour of screen.
For capturing screen image I am doing as follows:
Robot robot = new Robot();
Rectangle captureSize = new Rectangle(screenBounds);
return robot.createScreenCapture(captureSize);
For manipulating images I am doing as follows:
image = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
if (true) {
int x = MouseInfo.getPointerInfo().getLocation().x - 25;
int y = MouseInfo.getPointerInfo().getLocation().y - 37;
Graphics2D graphics2D = sourceImage.createGraphics();`enter code here`
graphics2D.drawImage(SimpleWebBrowserExample.m_MouseIcon, x, y, 48, 48, null);
}
image.getGraphics().drawImage(sourceImage, 0, 0, null);
return image;
please tell me how to get the images with colour same as actual colour on screen.
Thanks.
Use BufferedImage.TYPE_INT_ARGB or BufferedImage.TYPE_INT_RGB, as shown in this example. If you need to change the colors, you can use a LookupOp with a four-component LookupTable that adjusts the alpha component as required for BufferedImage.TYPE_3BYTE_BGR: "When data with non-opaque alpha is stored in an image of this type, the color data must be adjusted to a non-premultiplied form and the alpha discarded." Examples may be found in Using the Java 2D LookupOp Filter Class to Process Images and Image processing with Java 2D.
See the the "pinkish" explanation here
Basically the image is saved as a ARGB and most viewers interpret it as a CMYK. Alpha is preserved when opening it back in Java, though.
I'm processing a bunch of images with some framework, and all I'm given is a bunch of BufferedImage objects. Unfortunately, these images are really dim, and I'd like to brighten them up and adjust the contrast a little.
Something like:
BufferedImage image = something.getImage();
image = new Brighten(image).brighten(0.3); // for 30%
image = new Contrast(image).contrast(0.3);
// ...
Any ideas?
That was easy, actually.
RescaleOp rescaleOp = new RescaleOp(1.2f, 15, null);
rescaleOp.filter(image, image); // Source and destination are the same.
A scaleFactor of 1.2 and offset of 15 seems to make the image about a stop brighter.
Yay!
Read more in the docs for RescaleOp.