tmariaz

tmariaz

Image comparison with existing list of images

I am uploading an image to the server. Before it gets uploaded I wanted to check whether I have already uploaded the same image before.

All the uploaded images can be accessed via the url https://myserver/image/{image_id}. As a current user I can get the list of images from the repo.

Now I can select the new image and it stores temporarily in the server. I wanted to compare new_image_url with the list of images stored in the server.

def compare_image(temp_img_url)
   # code to get the list of images from repo
   Enum.each(images, fn m ->
     user_image = “https://myserver/image/#{m.id}”
     {:ok, duplicate_image} = compare_images_from_url(temp_img_url, user_image)
   end)
end

def compare_images_from_url(tmp, current) do
    response = HTTPoison.get!(image_url, [], hackney: [recv_timeout: 15_000, timeout: 150_000])

    case response.status_code do      
         # logics to compare the images 
         # .....
         # return true if there is a duplicate
    end
end

There is a possibility that a user can have 1000s of images. But it is not ideal to compare every single image to find that. Which sounds costly.

What is the best way to do this?

Most Liked

kip

kip

ex_cldr Core Team

The general strategy for “image is basically the same” is to use a perceptual hash. Image.dshash/1 will generate such a hash which can stored in the database if you need, for each image. Or just use the hash to compare with the hash of other images.

A perceptual hash overcomes the issues of same image but different resolution, or different image format, or different image colorspace. But still the “same” image.

Something like:

def kinda_the_same?(image_1, image_2) do
  Image.dhash(image_1) == Image.dhash(image_2)
end
kip

kip

ex_cldr Core Team

Yes, you are right. Basically the image is resized, converted to BW, convolved to sharpen the edges and that’s basically the “image hash”. You can see the code here.

Im not 100% happy with the implementation but as best I can test it works as expected (please open issues if you find otherwise). I’ve fixed the implementation to return the expected 64-bit hash (not the previous 512-bit hash which was wasting space).

kip

kip

ex_cldr Core Team

Probably slower because it involves image resizing, edge detection, contrast enhancement. But its not testing for identical. Identical is not very meaningful for image comparison since different compression algorithms and settings mean the image doesn’t round trip after decoding.

You can use mean square error as a way to establish “similarity” between two images. I use this in the test suite to overcome some the challenges - there can be different results across different library builds, system architectures and so on.

Basically the code for image similarity is:

    similarity =
      calculated_image
      |> Math.subtract!(validate_image)
      |> Math.pow!(2)
      |> Vix.Vips.Operation.avg!()

Note the operations are matrix operations since an image is basically just a matrix.

D4no0

D4no0

I think different image size can be handled easily by any fingerprinting algorithms, it gets tricky when the image is partially manipulated.

kip

kip

ex_cldr Core Team

Very likely a javascript implementation (almost certain).

Yes, deliberately intended to be resolution independent. And format independent. And colourspace independent.

Where Next?

Popular in Questions Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New

Other popular topics Top

peerreynders
Manning 2016 Halloween weekend sale via Deal of the Day Friday, October 28 - Half off all MEAPs - code WM102816LT Saturday, October 29 ...
326 29600 154
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement