I am not new to bitmaps nor new to java. I am trying to convert High resolution bitmaps to byte array in a loop. Please find code here:
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream);
imageByteArray = stream.toByteArray();
When i am using the above approach I cam able to convert 5 images in 1 second. But I need it to be even faster. I tried ByteBuffer approach also like this:
Bitmap bmp = intent.getExtras().get("data");
int size = bmp.getRowBytes() * bmp.getHeight();
ByteBuffer b = ByteBuffer.allocate(size);
bmp.copyPixelsToBuffer(b);
byte[] bytes = new byte[size];
try {
b.get(bytes, 0, bytes.length);
} catch (BufferUnderflowException e) {
// always happens
}
But this is very slow (Slower then previous) :(
Please, can somebody give a faster method? Guide Me...
The first solution is the right one.
But two things can happen here:
The image is maybe not of JPEG type, so conversion occurs, which takes time
The image is compressed 50%, which takes time
That aside, if it's taking some time, I doubt it could go faster (being the right solution).
You must consider the fact that the speed of processing is tightly tied to the speed of the device you are testing on( since this is tagged android I'm presuming you're using a mobile device ).
You should take a look at android developer on how to handle large bitmaps effectively Android developers . Since processing 5 high resolution images per second is slow to you I can presume you are having some kind of gallery or previews? If that's the case you shouldn't handle the high resolution images and should indeed take a look at the link above.
Also as a side-note your second code can be optimised this way:
int bytes = bmp.getByteCount();
ByteBuffer buffer = ByteBuffer.allocate(bytes);
bmp.copyPixelsToBuffer(buffer);
byte[] array = buffer.array();
Otherwise the most efficient way of copying bytes that I know is copy() taken from Commons-IO:
public static int copy(InputStream input, OutputStream output) throws IOException {
int n, count = 0;
byte[] buffer = new byte[4 * 1024];
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
you can try as follows
Bitmap bitmap = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap .compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
hope it may work good for you!!
check for line bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream); it may cause problem..as you are using JPEG format with rate 50.
Related
This question already has answers here:
Decrease image size without losing its quality in android
(6 answers)
Closed 4 years ago.
When the user selects an image from the gallery, I convert it into a base64 string. Then I use an API. The image size must be less than 2MB, so if the image from the gallery is bigger than 2MB, I need to reduce its size.
How should I do that?
That's my code:
// I pass the URI of the image to the method
#Override
protected Integer doInBackground(Uri... uris) {
InputStream inputStream = null;
try {
inputStream = getContentResolver().openInputStream(uris[0]);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] buffer = new byte[8192];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
bytes = output.toByteArray();
String encodedImage = Base64.encodeToString(bytes, Base64.NO_WRAP);
You can create a Bitmap object using
BitmapFactory.decodeByteArray
Decode an immutable bitmap from the specified byte array.
and then you can use
bitmap.compress
Write a compressed version of the bitmap to the specified
outputstream. If this returns true, the bitmap can be reconstructed by
passing a corresponding inputstream to BitmapFactory.decodeStream().
Note: not all Formats support all bitmap configs directly, so it is
possible that the returned bitmap from BitmapFactory could be in a
different bitdepth, and/or may have lost per-pixel alpha (e.g. JPEG
only supports opaque pixels).
This method may take several seconds to complete, so it should only be
called from a worker thread.
and/or
Bitmap.createScaledBitmap
Creates a new bitmap, scaled from an existing bitmap, when possible.
If the specified width and height are the same as the current width
and height of the source bitmap, the source bitmap is returned and no
new bitmap is created.
In my project I created two utility functions that might help you understand how to use the functions I mentioned above to fit your needs
public static Bitmap base64ToBitmap(String encodedImage) {
byte[] decodedString = Base64.decode(encodedImage, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
}
public static String bitmapToBase64(Bitmap bitmap, int quality, int percentScale) {
if (quality < 0 || quality > 100) {
quality = IMAGE_QUALITY_DEFAULT_VALUE;
}
if (percentScale < 0 || percentScale > 100) {
percentScale = IMAGE_SCALE_DEFAULT_VALUE;
}
float scale = (float) percentScale / 100;
int width = Math.round(bitmap.getWidth() * scale);
int height = Math.round(bitmap.getHeight() * scale);
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
return Base64.encodeToString(byteArray, Base64.NO_WRAP);
}
One way to easily check the image and store it is according to the developer docs
https://developer.android.com/topic/performance/graphics/load-bitmap#java
Which gives a solution to your answer explained properly and another way is to change the size of the image which can be found here and then use it accordingly
https://developer.android.com/reference/android/graphics/drawable/ScaleDrawable
Welcome
I need to download sycnronously (one at time) a lot of small remote images (between 50kb and 100kb) from a server and to store them as PNG in the device. I need to achieve this without third party libraries and I'm using this code but it is too munch slow:
URL javaUrl = new URL(URLParser.parse(this.url));
URLConnection connection = javaUrl.openConnection();
InputStream input = new BufferedInputStream(javaUrl.openStream());
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
output.write(data, 0, count);
}
// conversion to bitmap
InputStream in = new ByteArrayInputStream(output.toByteArray());
Bitmap original = BitmapFactory.decodeStream(in);
// storing bitmap as PNG file
FileOutputStream out = new FileOutputStream(filename);
original.compress(Bitmap.CompressFormat.PNG, 90, out);
output.flush();
output.close();
input.close();
in.close();
original.recycle();
The problem is that the download is very slow. With very fast WIFI internet in the device (13MB, download speed of 1.4mbytes/s), it is taking 3-4 seconds to download the image in the device, but only 100-200ms to download the image in my PC using google chrome for example.
It is something wrong in my download algorithm? can be improved?
Thanks
You have a totally unnecessary byte array in the middle.
BitmapFactory.decodeStream() accepts an InputStream and you get an InputStream from URL.openStream().
It might not give you the speed boost you're looking for, but it'll at least get rid of a completely useless step in your code.
OK, I know this is a bit of a weird question:
I'm writing this piece of java code and need to load raw data (approx 130000 floating points):
This data never changes, and since I don't want to write different loading methods for PC and Android, I was thinking of embedding it into the source file as a float[].
Too bad, there seems to be a limit of 65535 entries; is there an efficient way to do it?
Store that data in a file in the classpath; then read that data as a ByteBuffer which you then "convert" to a FloatBuffer. Note that the below code assumes big endian:
final InputStream in = getClass().getResourceAsStream("/path/to/data");
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final byte[] buf = new byte[8192];
int count;
try {
while ((count = in.read(buf)) != -1)
out.write(buf, 0, count);
} finally {
out.close();
in.close();
}
final FloatBuffer buf = ByteBuffer.wrap(out.toByteArray()).asFloatBuffer();
You can then .get() from the FloatBuffer.
You could use 2 or 3 arrays to get around the limit, if that was your only problem with that approach.
I want to know how to get the size of an image header. I have been searchin in internet but I have not found information about how to get an image (bitmap) header in java/android.
Anyone has an idea or example?
What I have been trying to do is next but it's not working properly because the image cannot be opened:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] b = baos.toByteArray();
byte[] b_header = new byte[8];
System.arraycopy(b, 0, b_header, 0, 8);
bos.write(b_header);
bos.flush();
bos.close();
I wouldn't suggest writing code to handle PNG manipulation yourself unless that is the main goal of the project. Instead, I'd find a library that would handle the loading of a PNG for you and then scramble the chunks that it can get for you. I found a nice PNG java library here:
PNGJ
Also, I wrote a small code snip based on their examples to get you on the right track to scrambling your images.
public static void encryptPng(File in, File out) {
PngReader pngReader = new PngReader(in);
PngWriter pngWriter = new PngWriter(out, pngReader.imgInfo, false);
pngWriter.copyChunksFrom(pngReader.getChunksList());
for (int row = 0; row < pngReader.imgInfo.rows; row++) {
ImageLineInt lineInt = (ImageLineInt) pngReader.readRow();
adjustLine(lineInt, pngReader.imgInfo);
pngWriter.writeRow(lineInt);
}
pngReader.close();
pngWriter.close();
}
public static void adjustLine(ImageLineInt line, ImageInfo info) {
/*
Scramble the line with some reversable algorithm so that it can't be read
*/
}
I see that a number of people have had a similar problem, however I'm yet to try find exactly what I'm looking for.
So, I have a method which reads an input image and converts it to a byte array:
File imgPath = new File(ImageName);
BufferedImage bufferedImage = ImageIO.read(imgPath);
WritableRaster raster = bufferedImage .getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
What I now want to do is convert it back into a BufferedImage (I have an application for which I need this functionality). Note that "test" is the byte array.
BufferedImage img = ImageIO.read(new ByteArrayInputStream(test));
File outputfile = new File("src/image.jpg");
ImageIO.write(img,"jpg",outputfile);
However, this returns the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: im == null!
This is because the BufferedImage img is null. I think this has something to do with the fact that in my original conversion from BufferedImage to byte array, information is changed/lost so that the data can no longer be recognised as a jpg.
Does anyone have any suggestions on how to solve this? Would be greatly appreciated.
This is recommended to convert to a byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", baos);
byte[] bytes = baos.toByteArray();
Note that calling close or flush will do nothing, you can see this for yourself by looking at their source/doc:
Closing a ByteArrayOutputStream has no effect.
The flush method of OutputStream does nothing.
Thus use something like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream(THINK_ABOUT_SIZE_HINT);
boolean foundWriter = ImageIO.write(bufferedImage, "jpg", baos);
assert foundWriter; // Not sure about this... with jpg it may work but other formats ?
byte[] bytes = baos.toByteArray();
Here are a few links concerning the size hint:
Java: Memory efficient ByteArrayOutputStream
jpg bits per pixel
Of course always read the source code and docs of the version you are using, do not rely blindly on SO answers.