I am using Xuggle to play interlaced video from ip-camera and really need in deinterlace option, which exists in FFMpeg (Xuggle is a java wrapper over FFMpeg library).
Unfortunately, deinterlace option ("-vf yadif", if i'm not mistake) is not exposed by Xuggle. So, i'm interested in either pure java solution of Java BufferedImage deinterlacing or some way to make Xuggle do that.
Now i simply duplicate odd lines and discard even. It is quick but quality of course is not very well.
// Interlaced image
BufferedImage img = (BufferedImage) event.getImage();
//duplicate odd lines
WritableRaster raster = img.getRaster();
for (int i = 0; i < raster.getHeight(); i += 2) {
int[] arr2 = null;
raster.setPixels(0, i + 1, raster.getWidth(), 1,
raster.getPixels(0, i, raster.getWidth(), 1,arr2));
}
//now img is deinterlaced
Could somebody help me in this regard?
I used FFMPEG's yadif filter through JNI. Java realisations were very slow.
Related
Nearest neighbor scaling works: The entire picture stays intact when I use TYPE_NEAREST_NEIGHBOR.
Even though it is Scala code, all used libraries are standard Java libraries.
Functions:
def getBufferedImage(imageFile: java.io.File): BufferedImage = {
ImageIO.read(imageFile)
}
def scaleImage(image: BufferedImage, minSize: Double): BufferedImage = {
val before: BufferedImage = image
val w = before.getWidth()
val h = before.getHeight()
val affit = new AffineTransform()
var scale = 1.0
if(h < w) {
if(h > 0) {
scale = minSize / h
}
} else {
if(w > 0) {
scale = minSize / w
}
}
affit.scale(scale, scale)
val affitop = new AffineTransformOp(affit, AffineTransformOp.TYPE_BICUBIC)
affitop.filter(before, null)
}
def getImageJpegByteArray(image: BufferedImage): Array[Byte] = {
val baos = new java.io.ByteArrayOutputStream()
val mcios = new MemoryCacheImageOutputStream(baos)
ImageIO.write(image, "jpeg", mcios)
mcios.close()
baos.toByteArray
}
Calling code snippet:
val img = getBufferedImage(imageFile)
val scaledImg = scaleImage(img, 512)
val result = getImageJpegByteArray(scaledImg)
// result is written to SQLite database
result is written to an SQLite database. If I download it from the database and save it as JPEG file, the resulting JPEG is
as expected if I use AffineTransformOp.TYPE_NEAREST_NEIGHBOR
completely black if I use AffineTransformOp.TYPE_BILINEAR
completely black if I use AffineTransformOp.TYPE_BICUBIC
Consequently, I accuse AffineTransformOp of being buggy...
How can I solve this problem?
File magic number of result is always ff d8 ff as expected for JPEG.
Details
Java version: Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_71
Operating System: Apple, OS X 10.9.5
Test image: http://www.photos-public-domain.com/wp-content/uploads/2012/05/thundercloud-plum-blossoms.jpg
I was able to reproduce your issue on Java 1.7.0_71 on OS X 10.10.4 (I rewrote your code in Java, I can post the full code if you are interested).
In any case, the problem is not that AffineTransformOp is buggy in itself. In my test program I displayed the image using a minimal Swing JFrame and the scaled image looked all good there. This is likely why most people in the comments did not understand the problem.
Part of the issue is that the BufferedImage returned by AffineTransformOp when you don't provide a destination to the filter method (the second parameter, null in your case), it will create one for you. This image will get type BufferedImage.TYPE_INT_ARGB. Here is the relevant code from AffineTransformOp.createCompatibleDestImage() (lines 456-468, I kept the formatting, to make it easier to spot):
ColorModel cm = src.getColorModel();
if (interpolationType != TYPE_NEAREST_NEIGHBOR &&
(cm instanceof IndexColorModel ||
cm.getTransparency() == Transparency.OPAQUE)
{
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
}
else {
image = new BufferedImage(cm,
src.getRaster().createCompatibleWritableRaster(w,h),
cm.isAlphaPremultiplied(), null);
}
Notice the special case for TYPE_NEAREST_NEIGHBOR, which explains why you'll get different behavior when using nearest neighbor algorithm.
Normally this is all good, however (as I said, the image displays just fine in a Swing component).
The problem arises when you try to store this image as a JPEG. During the years, there's been a lot of confusion and issues related to the ImageIO JPEG plugin and whether it will allow you to write images with alpha channel (like your TYPE_INT_ARGB image). It does allow that. But, most often ARGB JPEGs will get misinterpreted as CMYK JPEGs (as they are 4 channels, and storing ARGB data in JPEG is very exotic) and will be displayed in all funky colors. In your case though, it seems to be all black...
So, there are two possible solutions:
Either write your image in a file format that supports alpha channel, like PNG or TIFF (TIFF requires an extra plugin, so it might not be the best choice). Like this:
ImageIO.write(image, "PNG", mcios);
Or, make sure your BufferedImage is in a pixel format without alpha channel before storing as JPEG. You can do this after the scaling, but the easiest (and fastest) is to just provide the AffineTransformOp with an explicit destination image, like this:
Rectangle newSize = affitop.getBounds2D(before).getBounds();
return affitop.filter(before,
new BufferedImage(newSize.width, newSize.height, BufferedImage.TYPE_3BYTE_BGR));
Here is your image, scaled by the program, using JPEG format and the TYPE_3BYTE_BGR:
I'm sure you can rewrite my Java code back to Scala. :-)
for my final year project I am developing an android app that can capture the image of a leaf and identify what type of tree it came from. I have a nearly completed PC version (developed in java) and i am starting the process of porting it to android. Although BufferedImage and Raster make up a key part of my program, this is a problem because java.awt is missing in android, so this means i have to alter the library.
I am using a library that was developed by my lecturer, the method below converts a BufferedImage to an IntImage(a type that is used throughout the package) using Raster.
I'm basically asking are there any android alternatives to java.awt that i can use?
Here's the code:
public static int[][] readAsInts(String fileName) throws IOException {
int[][] pixels;
System.out.println("File: " + fileName);
String [] types = ImageIO.getReaderFileSuffixes();
for (int l = 0; l < types.length; l++) {
System.out.println("Type " + l + ": " + types[l]);
}
File f = new File(fileName);
BufferedImage bi;
bi = ImageIO.read(f);
int cols = bi.getWidth();
int rows = bi.getHeight();
System.err.println("Number of bands: " + bi.getRaster().getNumBands());
Raster rast = bi.getRaster();
pixels = new int[rows][cols];
for (int r = 0; r < rows; r++) {
rast.getSamples(0, r, cols, 1, 0, pixels[r]);
}
return pixels;
} // readAsInts
Thanks guys!
EDIT: For anyone who doesn't know the raster stores the pixel values for the BufferedImage, along with the number of bands (red, green, blue, alpha), so this algorithm takes the pixel values ad stores them in a 2d array (pixels).
Sounds like you just want to work with bitmaps and be able to manipulate the pixels themselves in an array. The Bitmap class comes to mind along with its getPixels method. Remember though that you have much more limited memory on a mobile device. It's easy to run into out of memory problems when working with large bitmaps.
Another route would be to use the NDK. I don't have much experience there, but AndroidBitmap_lockPixels and AndroidBitmap_unlockPixels are probably a good place to start.
Lastly, yet another option that should perform pretty well is to use RenderScript.
If all you're looking to do is load an image into an int array though, Bitmap.getPixels should be quite sufficient.
You can convert a BufferedImage to a PNG or JPEG, then send it via HTTP to Android which can then convert the PNG or JPEG back to a Bitmap and use it.
When Android needs to send any image, send it as PNG or JPEG via network, and then convert the PNG to BufferedImage and use it in your program.
I am working with QRCode api found here
I successfully implemented the QRCode generation through Api but the result is this
(I changed the color from White to yellow in order to ask my question)
So, now if you see you find that the outer boundary is very thick I want it to be thin..like this
The code I used to generate the qrcode and changing the color is this
public boolean writeImage(String qrMessageForGeneratingQRCode,String filename){
boolean result = false;
try{
int length = 200;
int breadth = 200;
BufferedImage originalQRCodeBufferedImage = ImageIO.read(new ByteArrayInputStream(QRCode.from(qrMessageForGeneratingQRCode).withSize(length,breadth).stream().toByteArray()));
BufferedImage changedQRCodeBufferedImage = new ColorChanger().changeColor(originalQRCodeBufferedImage, Color.WHITE, new Color(255,202,0));
ImageIO.write(changedQRCodeBufferedImage,FilesUtil.getProperty("QR_CODE_IMAGE_FORMAT") , new File(filename));
result = true;
}catch(Exception ex){
ex.printStackTrace();
result = false;
}
return result;
}
Please shed some light how can I achieve that using code....Thanks in advance....
Ankur
The quiet zone around the QR code needs to be 4 modules. You don't want to reduce this. Your proposed image will be harder to scan.
You can always edit the BufferedImage after the fact with a simple crop:
BufferedImage crop = original.getSubimage(50, 50, original.getWidth() - 2*50, original.getHeight() - 2*50);
BTW you should update the underlying zxing library used in your solution to at least 2.0, probably 2.1-SNAPSHOT. 1.7 is old.
Obvious answer would be to reduce the length and breadth values. Did you try that?
I have a method converting BufferedImages who's type is TYPE_CUSTOM to TYPE_INT_RGB. I am using the following code, however I would really like to find a faster way of doing this.
BufferedImage newImg = new BufferedImage(
src.getWidth(),
src.getHeight(),
BufferedImage.TYPE_INT_RGB);
ColorConvertOp op = new ColorConvertOp(null);
op.filter(src, newImg);
It works fine, however it's quite slow and I am wondering if there is a faster way to do this conversion.
ColorModel Before Conversion:
ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace#1c92586f transparency = 1 has alpha = false isAlphaPre = false
ColorModel After Conversion:
DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=0
Thanks!
Update:
Turns out working with the raw pixel data was the best way. Since the TYPE_CUSTOM was actually RGB converting it manually is simple and is about 95% faster than ColorConvertOp.
public static BufferedImage makeCompatible(BufferedImage img) throws IOException {
// Allocate the new image
BufferedImage dstImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
// Check if the ColorSpace is RGB and the TransferType is BYTE.
// Otherwise this fast method does not work as expected
ColorModel cm = img.getColorModel();
if ( cm.getColorSpace().getType() == ColorSpace.TYPE_RGB && img.getRaster().getTransferType() == DataBuffer.TYPE_BYTE ) {
//Allocate arrays
int len = img.getWidth()*img.getHeight();
byte[] src = new byte[len*3];
int[] dst = new int[len];
// Read the src image data into the array
img.getRaster().getDataElements(0, 0, img.getWidth(), img.getHeight(), src);
// Convert to INT_RGB
int j = 0;
for ( int i=0; i<len; i++ ) {
dst[i] = (((int)src[j++] & 0xFF) << 16) |
(((int)src[j++] & 0xFF) << 8) |
(((int)src[j++] & 0xFF));
}
// Set the dst image data
dstImage.getRaster().setDataElements(0, 0, img.getWidth(), img.getHeight(), dst);
return dstImage;
}
ColorConvertOp op = new ColorConvertOp(null);
op.filter(img, dstImage);
return dstImage;
}
BufferedImages are painfully slow. I got a solution but I'm not sure you will like it. The fastest way to process and convert buffered images is to extract the raw data array from inside the BufferedImage. You do that by calling buffImg.getRaster() and converting it into the specific raster. Then call raster.getDataStorage(). Once you have access to the raw data it is possible to write fast image processing code without all the abstraction in BufferedImages slowing it down. This technique also requires an in depth understanding of image formats and some reverse engineering on your part. This is the only way I have been able to get image processing code to run fast enough for my applications.
Example:
ByteInterleavedRaster srcRaster = (ByteInterleavedRaster)src.getRaster();
byte srcData[] = srcRaster.getDataStorage();
IntegerInterleavedRaster dstRaster = (IntegerInterleavedRaster)dst.getRaster();
int dstData[] = dstRaster.getDataStorage();
dstData[0] = srcData[0] << 16 | srcData[1] << 8 | srcData[2];
or something like that. Expect compiler errors warning you not to access low level rasters like that. The only place I have had issues with this technique is inside of applets where an access violation will occur.
I've found rendering using Graphics.drawImage() instead of ColorConvertOp 50 times faster. I can only assume that drawImage() is GPU accelerated.
i.e this is really slow, like 50ms a go for 100x200 rectangles
public void BufferdImage convert(BufferedImage input) {
BufferedImage output= new BufferedImage(input.getWidht(), input.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CUSTOM_PALETTE);
ColorConvertOp op = new ColorConvertOp(input.getColorModel().getColorSpace(),
output.getColorModel().getColorSpace());
op.filter(input, output);
return output;
}
i.e however this registers < 1ms for same inputs
public void BufferdImage convert(BufferedImage input) {
BufferedImage output= new BufferedImage(input.getWidht(), input.getHeight(), BufferedImage.TYPE_BYTE_BINARY, CUSTOM_PALETTE);
Graphics graphics = output.getGraphics();
graphics.drawImage(input, 0, 0, null);
graphics.dispose();
return output;
}
Have you tried supplying any RenderingHints? No guarantees, but using
ColorConvertOp op = new ColorConvertOp(new RenderingHints(
RenderingHints.KEY_COLOR_RENDERING,
RenderingHints.VALUE_COLOR_RENDER_SPEED));
rather than the null in your code snippet might speed it up somewhat.
I suspect the problem might be that ColorConvertOp() works pixel-by-pixel (guaranteed to be "slow").
Q: Is it possible for you to use gc.createCompatibleImage()?
Q: Is your original bitmap true color, or does it use a colormap?
Q: Failing all else, would you be agreeable to writing a JNI interface? Either to your own, custom C code, or to an external library such as ImageMagick?
If you have JAI installed then you might try uninstalling it, if you can, or otherwise look for some way to disable codecLib when loading JPEG. In a past life I had similar issues (http://www.java.net/node/660804) and ColorConvertOp was the fastest at the time.
As I recall the fundamental problem is that Java2D is not at all optimized for TYPE_CUSTOM images in general. When you install JAI it comes with codecLib which has a decoder that returns TYPE_CUSTOM and gets used instead of the default. The JAI list may be able to provide more help, it's been several years.
maybe try this:
Bitmap source = Bitmap.create(width, height, RGB_565);//don't remember exactly...
Canvas c = new Canvas(source);
// then
c.draw(bitmap, 0, 0);
Then the source bitmap will be modified.
Later you can do:
onDraw(Canvas canvas){
canvas.draw(source, rectSrs,rectDestination, op);
}
if you can manage always reuse the bitmap so you be able to get better performance. As well you can use other canvas functions to draw your bitmap
I am using Java with swing in FSE mode. I want to load a completely black-and-white image into binary format (a 2d array preferably) and use it for mask-based per-pixel collision detection. I don't even know where to start here, I've been researching for the past hour and haven't found anything relevant.
Just read it into a BufferedImage using ImageIO#read() and get the individual pixels by BufferedImage#getRGB(). A value of 0xFFFFFFFF is white and the remnant is color. Assuming that you want to represent white as byte 0 and color (black) as byte 1, here's a kickoff example:
BufferedImage image = ImageIO.read(new File("/some.jpg"));
byte[][] pixels = new byte[image.getWidth()][];
for (int x = 0; x < image.getWidth(); x++) {
pixels[x] = new byte[image.getHeight()];
for (int y = 0; y < image.getHeight(); y++) {
pixels[x][y] = (byte) (image.getRGB(x, y) == 0xFFFFFFFF ? 0 : 1);
}
}
See also:
The Java Tutorials - 2D Graphics - Working with images
If you're reading the image from a URL, it will already be in a binary format. Just download the data and ignore the fact that it's an image. The code which is involved in download it won't care, after all. Assuming you want to write it to a file or something similar, just open the URLConnection and open the FileOutputStream, and repeatedly read from the input stream from the web, writing the data you've read to the output stream.
You can also use ImageIO if you are not downloading it from some resource.