how to compare two images, to know are they similar for 100%?
I was getting path of all images from mediastore, then converted to bitmap and compared using bitmap.sameAs(bitmapToCompare), but it takes to much memory and got outofmemory exepcetion
Now i am trying to use OpenCv library as:
val img1: Mat = Imgcodecs.imread(it)
val img2: Mat = Imgcodecs.imread(it1)
val result = Mat()
Core.compare(img1, img2, result, Core.CMP_EQ)
val ines = Core.countNonZero(result)
if(ines==0){
//similar images
}
but get an error in Core.countNonZero as following:
cv::Exception: OpenCV(4.5.3) /home/quickbirdstudios/opencv/releases/opencv-4.5.3/modules/core/src/count_non_zero.dispatch.cpp:128: error: (-215:Assertion failed) cn == 1 in function 'countNonZero'
so what is best way to compare two images?
First off, let's correct you. Neither your OpenCV snippet not Android can directly compare if two images are "similar". They can compare if they are exactly the same. That's not the same thing. You'd have to decide if its good enough.
Secondly, OpenCV is overkill for this. If the two images are Bitmaps in memory, just loop over the byte arrays of the two files. If they're on disk, just compare the byte by byte data of the two files.
You said you "got the paths of all images, then converted to Bitmap". Yeah, that would take a ton of memory. Instead, if you want to compare all the files, do this:
val map = mutableMapOf()
fileNames.each {
val hash = hash_file(it)
if (map.contains(hash)) {
//In this case, the two file stored in it and map[hash] are the same
}
else {
map[hash] = it
}
}
Here hash_file is any well known hash function. MD5 would work fine.
Now if you actually want similarity- good luck, you're going to need to learn a lot of AI and machine learning to determine that. Or find someone who already has a model for an appropriate training set.
Related
I've made an Image classification model and converted it to tflite format.
Then I've verified tflite model in Python using tf.lite.Interpreter — it produces same results for my test image as the original model. Here's a colab link to verify.
Then I embedded it to a sample Android app, using Android Studio ML Model Binding and exact example code from Android studio.
Here's the main activity code, you can also use this link to navigate to the full android project.
val assetManager = this.assets
val istr = assetManager.open("test_image.JPG") //The same image
val b = BitmapFactory.decodeStream(istr)
val model = Model2.newInstance(this) //Model definition generated by Android Studio
// Creates inputs for reference.
val image = TensorImage.fromBitmap(b)
// Runs model inference and gets result.
val outputs = model.process(image)
val probability = outputs.probabilityAsCategoryList
probability.sortByDescending { it.score }
val top9 = probability.take(9)
this.findViewById<TextView>(R.id.results_text).text = top9.toString()
And then I'm getting completely different results on Android for the same model and the same input image.
Here are results matching my initial model in Python:
Here are wrong results I'm getting in Android app:
Links to the model and the test image are there in both examples, but I'll post them into the question once again:
tflite model
test image
I guess it has something to do with input/output formats of the model. Or the image is interpreted differently in python and in android. Or the metadata I added to the model is somehow wrong. Anyways, I've tried everything to localize the issue and now I'm stuck.
How do I fix my model or Android code so it produces the same results as my python code?
I've managed to find and fix the issue:
My model from this tutorial included a built-in image normalization layer. Image normalization is when you transform standard 0-255 image color values to 0.0-1.0 float values, suitable for machine learning.
But the metadata I used for the tflite model included 2 parameters for external normalization: mean and std.
Formula for each value being: normalized_value = (value - mean) / std
Since my model handles its own normalization, I need to turn off external normalization by setting mean = 0 and std = 1.
This way I'll get normalized_value = value.
So, setting the tflite metadata parameters to these:
image_min=0,
image_max=255.0,
mean=[0.0],
std=[1.0]
fixed the double normalization issue and my model now produces correct results in Android app.
I recently had the task of performing a cross-selection operation on some collections, to find an output collection that was matching my criteria. (I will omit the custom logic because it is not needed).
What I did was creating a class that was taking as a parameter Lists of elements, and I was then calling a function inside that class that was responsible for processing those lists of data and returning a value.
Point is, I'm convinced I'm not doing the right thing, because writing a class holding hundreds of elements, taking names lists as parameters, and returning another collection looks unconventional and awkward.
Is there a specific programming object or paradigm that allows you to process large numbers of large collections, maybe with a quite heavy custom selection/mapping logic?
I'm building for Android using Kotlin
First of all, when we talk about the performance, there is only one right answer - write benchmark and test.
About memory: list with 1,000,000 of unique Strings with average size 30 chars will take about 120 Mb (e.g. 10^6 * 30 * 4, where last is "size of char", let's think that this is Unicode character with 4 bytes). And please add 1-3% for collateral expenses, such as link references. Therefore: if you have hundreds of Strings then just load whole data into memory and use list, because this is the fastest solution (synchronous, immutable, etc.).
If you can do streaming-like operations, you can use sequences. They are pretty lazy, the same with Java Streams and .Net Linq. Please check example below, it requires small amount of memory.
fun countOfEqualLinesOnTheSamePositions(path1: String, path2: String): Flow<String> {
return File(path1).useLines { lines1 ->
File(path2).useLines { lines2 ->
lines1.zip(lines2)
.map { (line1, line2) ->
line1 == line2
}
.count()
}
}
}
If you couldn't store whole data in memory and you couldn't work with stream-like schema, you may:
Rework algorithm to single-pass to multiple-pass, there each is stream-like. For example, Huffman Coding is two-pass algorithm, so it can be used to compress 1Tb of data by using small amount of memory.
Store intermediate data on the disk (this is much complex for this short answer).
For additional optimizations:
To cover case of merging a lot of parallel streams, please consider also Kotlin Flow. It allows you to work asynchronously, to avoid IO blocks. For example, this can be useful to merge ~100 network streams.
To keep a lot of non-unique items in memory, please consider caching logic. It can save memory (however please benchmark first).
Try operate with ByteBuffers, instead of Strings. You can get much less allocation (because you can deallocate object explicitly), however code will be too complex.
So, I'm using a file sharing app on Android. It creates a duplicate copy which is uploaded to it's server.
PROBLEM
The following code works for a duplicate copy I manually create. That is, I long press and copy the file into the same directory with a File Manager. Then my function returns true. When it compares the duplicate image due to the app and the original image, I get false.
MD5-checksums are different so that is out of the options.
CODE
public boolean equals(Bitmap bitmap1, Bitmap bitmap2) {
ByteBuffer buffer1 = ByteBuffer.allocate(bitmap1.getHeight()
* bitmap1.getRowBytes());
bitmap1.copyPixelsToBuffer(buffer1);
ByteBuffer buffer2 = ByteBuffer.allocate(bitmap2.getHeight()
* bitmap2.getRowBytes());
bitmap2.copyPixelsToBuffer(buffer2);
return Arrays.equals(buffer1.array(), buffer2.array());
}
Here are the images :
Original image -
Duplicate image created by the app -
My code currently returns false while comparing these two images.
How do I get the code to return true?
Your problem is due to artefacts created by JPEG compression, if you can always keep the images in PNG then your problem is most likely solved. If you can't do that, then you need a better algorithm to compare the images. This is exactly the same problem discussed at Comparing image in url to image in filesystem in python
For instance, running the algorithms mentioned in the earlier discussion, we get a similarity of more than 99%. With that similarity value, you can say the images are the same.
I'm working with Android and I really need a fast way to get a bitmap of format BGRA to be filled in ARGB.
One thing I aslo want to let u know that The Data comes in byte[] format and I have to convert in int[] format also.
Can AnyOne Tell me How to do this ...
Thanks in Advance
If you want to load a Bitmap by bytestream, you can use Bitmap.decodeStream. You could then use getPixel(s?) to get the int array. This is one way I know how to do this, probably not the fastest though. A faster way would be to convert bytes to int, if your byte array is nothing but pixeldata this won't be too hard.
BGRA to ARGB can be done with bitshifting quite fast.
A nice source you would probably like:
https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx
The fastest way I think would be to do it in native code using the NDK. I've been considering to use it for image processing for some time but didn't get the chance yet. So I don't know much about it (i.e. how you would access your byte buffer) but you could start from the Plasma sample for bitmap processing in JNI.
I'm a flash developer with no previous Java experience, just starting to learn android development. I'm trying to make a simple kid's flash cards app, consisting of a load of images of animals, and a load of sounds that they make.
Currently I have the images in a gallery view, storing them in an array. I also have an array of the sounds. So each image and the corresponding sound are in the same position in the array, so it's easy to play the right sound for the right image.
Now I want to shuffle the cards so that they appear in a different order each time the app is started up. I managed to shuffle the arrays into a random order, but kept the images and sounds in the same positions in each array but I can feel this getting messy and I'm sure this isn't the best way to go about this problem.
If this were a flash movie, I'd use objects to link the images and sounds and stick the objects in an array. Can anyone help me with some code which would achieve the same thing for android? Bear in mind I'm a complete beginner with Java and have gotten this far with tutorials and basic concepts being the same as AS3.
I'd use objects to link the images and sounds and stick the objects in an array.
Me too. Just create a class to wrap animals and sounds:
class SomeNiceName{
private final Bitmap animal;
// I'm guessing the sound is in the resources
// folder, thus you only need an integer to reference it
private final int sound;
public Animal(Bitmap animal, int sound){
this.animal = animal;
this.sound = sound;
}
public Bitmap getAnimal(){
return animal;
}// missing getter for sound
}
In this case I'm using an immutable object which is convenient in this case. Then you can create an array of those animals, o better yet a list:
// array
SomeNiceName[] array = new SomeNiceName[blah];
array[0] = new SomeNiceName(someBitmap, theSound);
// or with lists:
List<SomeNiceName> list = new ArrayList<SomeNiceName>();
list.add(new SomeNiceName(someBitmap, theSound));
The only thing you would have to "disorder" in this case is one array.
As Christian said you can of course use class in your Android application.
But, since mobile devices haven't huge processing capabilities like desktops or laptops -yet-, I advice you to read the articles below before running your OOP habits ;)
Object Creation
Getters/Setters? Not here!
For items above and more...