Android compare/search multiple images - android

I got a list of images and an input image, I want to output true if the image exist in my list.
Images will consider to be equal if they got the same raw data.
Is there an easy way of doing this in Android?
I saw this method:
boolean Bitmap.sameAs(Bitmap)
But it require to test binary data against each of my images in the list and this is kind of expensive. Is there a better way doing this?
And what if I change the definition of equal images to "similar", in such way that if the images got over 90% visual similarity they will consider to be equal.

Here is the solution for the first part where equal images are those who got the same raw data.
You can hash each image and compare it by hash, this way you will test the image raw data only once.
String toSHA1(Bitmap bitmap){
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
try {
String sha1 = toSHA1(byteArray);
stream.close();
return sha1;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
String toSHA1(byte[] data) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-1");
BigInteger bigInteger = new BigInteger(1, md.digest(data));
return bigInteger.toString(Character.MAX_RADIX);
}

Related

Image size increased more after convert to Base64

I am using Compressor third party library for compress the captured images size its working fine and now size is showing KB's but when i convert this images to BASE64 file size becomes 6MB or more size showing my code is below can some one help me please what should i do for resolve this issue
code:
File file= new Compressor(this).compressToFile(f);
String base64File = getBase64StringFile(file);
// Converting File to Base64.encode String type using Method
public static String getBase64StringFile(File f) {
InputStream inputStream = null;
String encodedFile= "", lastVal;
try {
inputStream = new FileInputStream(f.getAbsolutePath());
byte[] buffer = new byte[10240];//specify the size to allow
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
Base64OutputStream output64 = new Base64OutputStream(output, Base64.DEFAULT);
while ((bytesRead = inputStream.read(buffer)) != -1) {
output64.write(buffer, 0, bytesRead);
}
output64.close();
encodedFile = output.toString();
}
catch (FileNotFoundException e1 ) {
e1.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
lastVal = encodedFile;
return lastVal;
}
You can resolve this issue using some other Compressor tools like FFMPEG.
Base64 always increase your file size
Base64 is often used on binary data that needs to be transmitted across a system that isn't really designed for binary. Depending on what you're doing, you may not even need to encode it. And per the wikipedia, on average, a file is expected to grow about 37% when you base64 encode it, which is almost exactly what your numbers are.

Why PNG forms black edges around the image for PNG format?

I select image from gallery, convert into Base64 and sends to server. For JPEG image, it works fine; the image I upload from gallery on server same gets shown in server folder. However, when I upload PNG format image from mobile gallery, it doesn't show same on server; instead it creates black edges around it. I really don't know what's going wrong?
Also, my actual image is as equal as given JPEG image.
Reference images:
JPEG:
PNG:
I just want to get rid of BLACK borders which should not appear for PNG format images.
Below is my code snippet
FileInputStream mFileInputStream = null;
try {
mFileInputStream = new FileInputStream(imagePathFromSDCard);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int bytesRead = 0;
while ((bytesRead = mFileInputStream.read(b)) != -1) {
bos.write(b, 0, bytesRead);
}
Bitmap bitmap = safeImageProcessing.decodeFile(uri);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
byte[] ba = bos.toByteArray();
String encodedImage = Base64.encodeToString(ba, Base64.NO_WRAP);
//this line sends image base64 to server & there i decode into original
new ImageAsync().sendImageProcess(getActivity(), encodedImage, this);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
You want to upload image files. But you use BitmapFactory to make a Bitmap out of them first. Then you compress the bitmap to a jpg or png byte array. After that you base64 encode the bytes to a string which you upload.
What a waiste. And you have changed the files. Do away with the intermediate bitmap.
Instead load the files directly in a byte array. Then continue with the byte array as usual.
Having said that i think its a bad idea to base64 encode the bytes of a file first as it increases the amount of bytes that have to be transferred with 30%.

Storing and retrieving app icons of all installed apps programmatically

I am building an app in which I programmatically collect details of all the apps installed on the device (appName, packageName, appIcon, and some other appDetails).
However, I observe that fetching and storing these app details in a data structure takes a perceivable amount of time. I have tried storing some of the app details using SharedPreferences so that I can retain data between different app executions and not initiate a fetch each and every time my app is launched.
Storing the app icons (drawables) however, takes up a lot of space and makes me wonder if there is a more efficient way of retaining the above mentioned app data between app executions, unless of course, new apps are installed after the last time the installed apps were programmatically fetched.
So, my question is, is there a efficient way of storing app icons (drawables) such that they don't take up too much space, and at the same time, the app set can be retained between different app executions?
I believe your best shot is saving the list of Apps installed in your App database. You can store the drawable icon of the app as encoded string.
String image = getImageFromDrawable(app.loadIcon(pm));
where app is ApplicationInfo and pm is PackageManager.
public String getImageFromDrawable(Drawable drawable){
String img = null;
if(drawable instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] arr = stream.toByteArray();
img = Base64.encodeToString(arr, Base64.URL_SAFE);
return img;
}
return null;
}
You can then store the details of the app in your application database.
This can be done by converting icons to String and storing them in a file using Parceler. To do so,
Create an App POJO class with icon as an attribute of Bitmap type
Make your class Parcelable
Initialize an ArrayList of this class type with all icons to be stored
Parcel and store these objects in a file
Now, attributes of type Bitmap are not Parcelable, so you will have to convert them to parcelable type. You may use this function for conversion:
static String bitMapToString(Bitmap bitmap) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
}
And the following function to convert the data back to Bitmap type.
static Bitmap stringToBitMap(String encodedString) {
try {
byte[] encodeByte = Base64.decode(encodedString, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
} catch (Exception e) {
e.getMessage();
return null;
}
}

Convert BufferedImage output from Java to a Bitmap in Android

Context
I believe I have searched enough about this topic but haven't found an answer yet. If any of you have any answer please help out.
I am trying to send an image as an output to a service call from an android app.
I have used BufferedImage, ImageIO and a ByteArrayOutputStream to convert the image to a Base64 encoded string (from many of the examples I could find on stackoverflow).
This Base64 encoded string is received by the android app. Now, this needs to be converted back to an image and shown on an activity.
FYI. The image I am trying to send over is a QR code generated using zxing libraries.
Problem
This is where I am struggling. I am not sure how to do it. I haven't posted any sample code, since the server side code is pretty self explanatory and I don't yet have any code on the server side.
Sample code on the Android side
private Bitmap getBitmap() {
String base64String="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";
ByteArrayInputStream imageArr=new ByteArrayInputStream(Base64.decode(base64String, Base64.DEFAULT));
Bitmap bitmap = BitmapFactory.decodeStream(imageArr);
return bitmap;
}
#haraldK, thanks for giving me the right pointer. I was also wondering why I didn't see any padding, but didn't put enough focus on it to understand the issue. But, based on your hint, I finally figured out the issue. The issue was that I was not closing the OutputStream given by Base64 Encoder properly. In my code I had a the Base64 encoder stream as a wrapper of a ByteArrayOutputStream as below:
final ByteArrayOutputStream os = new ByteArrayOutputStream();
String qString = "";
try {
MatrixToImageWriter.writeToStream(matrix, formatName, Base64.getEncoder().wrap(os));
qString = os.toString("UTF-8");
os.close();
return qString;
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
In the above code, I was closing the ByteArrayOutputStream using os.close() which was not required. But what was required was closing of the OutputStream given out by the Base64 encoder. I changed the code as below to make it work:
final ByteArrayOutputStream os = new ByteArrayOutputStream();
OutputStream base64Stream = Base64.getEncoder().wrap(os);
String qString = "";
try {
MatrixToImageWriter.writeToStream(matrix, formatName, base64Stream);
base64Stream.close();
qString = os.toString("UTF-8");
os.close();
return qString;
} catch (final IOException ioe) {
throw new UncheckedIOException(ioe);
}
After the above change, the base64 encoded strings are properly padded and are properly getting decoded on the Android side. On the android side this is code I have to display the image:
byte[] imageArr=Base64.decode(base64String.getBytes(), Base64.NO_WRAP);
Bitmap bitmap=BitmapFactory.decodeByteArray(imageArr, 0, imageArr.length);
return bitmap;
I didn't know how to upvote your answer, so I have created this as a separate answer. Thanks for your help.
Can you please add your image Response string
As you are doing it in ByteArrayOutputStream you may missed the below item before decoding
String imageDataBytes = responseImageData.substring(responseImageData.indexOf(",")+1);
InputStream stream = new ByteArrayInputStream(Base64.decode(imageDataBytes.getBytes(), Base64.DEFAULT));
Bitmap bitmap = BitmapFactory.decodeStream(stream);
A good answer available for the same question here

Converting byte[] to Base64 String throws OutOfMemoryError

I'm trying to convert files to Base64 string. For small size file it works perfectly, for files larger like 500mb it throws OutOfMemoryError. I'm to convert the file to Base64 encodedString because it is my server side requirement to upload a file through Base64 encodedstring. is it possible to convert and send a 500mb file through this method ? Thanks in advance.
byte[] bytes = null;
InputStream inputStream;
try {
inputStream = new FileInputStream(mFilepath);
byte[] buffer = new byte[1024];
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while ((bytesRead = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
inputStream.close();
output.flush();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
bytes = output.toByteArray();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
// Here it throws OutOfMemoryError
String encodedString = Base64.encodeToString(bytes, Base64.DEFAULT);
Then I'm passing encodedString to server using HttpURLConnection.
for files larger like 500mb it throws OutOfMemoryError
Of course. The heap limit of a Java-based Android app is going to be a lot smaller than 500MB, let alone two copies of the data, one in an expanded (Base64) format. There are hundreds of millions of devices that do not even have that much RAM for the whole device, let alone for use by your app.
is it possible to convert and send a 500mb file through this method ?
Only if you can somehow stream up the converted bytes. Convert a handful of bytes, write them to the socket, convert the next handful of bytes, write them to the socket, and so forth. You have no practical way of converting the entire 500MB file into Base64 in memory and transferring it as a string.

Categories

Resources