Skip to content

oklookat/images4kt

 
 

Repository files navigation

Find similar images with Kotlin

Fork of https://github.com/vitali-fedulov/images4

Installation

I haven't figured out how to publish Java projects, so there are two installation options:

  1. Download the .jar from the releases.
  2. Include this project in your project.

Example of comparing 2 images

import ru.oklookat.images4kt.Image
import ru.oklookat.images4kt.ImageFactory
import ru.oklookat.images4kt.ImageType
import ru.oklookat.images4kt.icon
import ru.oklookat.images4kt.similar
import java.awt.image.BufferedImage
import java.net.URL
import javax.imageio.ImageIO

class Img(private val buff: BufferedImage) : Image {
    override fun getRGB(x: Int, y: Int): Int {
        return buff.getRGB(x, y)
    }

    override fun setRGB(x: Int, y: Int, rgb: Int) {
        buff.setRGB(x, y, rgb)
    }

    override val width: Int = buff.width
    override val height: Int = buff.height
}

class ImgFact : ImageFactory {
    override fun make(width: Int, height: Int, imgType: ImageType): Image {
        var type = 0
        when (imgType) {
            ImageType.INT_ARGB -> type = BufferedImage.TYPE_INT_ARGB
        }
        return Img(BufferedImage(width, height, type))
    }

}

fun compare() {
    // Photos to compare.
    val photo1 = URL("https://upload.wikimedia.org/wikipedia/en/c/c1/The_Weeknd_-_After_Hours.png")
    val photo2 = URL("https://upload.wikimedia.org/wikipedia/en/2/29/BornToDieAlbumCover.png")

    // Read images.
    val img1 = Img(ImageIO.read(photo1))
    val img2 = Img(ImageIO.read(photo2))

    val fact = ImgFact()
    // Icons are compact image representations (image "hashes").
    val icon1 = icon(fact, img1)
    val icon2 = icon(fact, img2)

    // Comparison. Images are not used directly. Icons are used instead, because they have tiny memory footprint and fast to compare. If you need to include images rotated right and left use func Similar90270.
    if (similar(icon1, icon2)) {
        println("Images are similar.")
    } else {
        println("Images are distinct.")
    }
}

Main functions

  • icon produces "image hashes" called "icons", which will be used for comparision.

  • icon.rotate90 turns an icon 90° clockwise. This is useful for developing custom similarity function for rotated images with eucMetric and propMetric. With the function you can also compare to images rotated 180° (by applying rotate90 twice).

  • similar gives a verdict whether 2 images are similar with well-tested default thresholds. To see the thresholds use defaultThresholds. Rotations and mirrors are not taken in account.

  • similar90270 is a superset of similar by additional comparison to images rotated ±90°. Such rotations are relatively common, even by accident when taking pictures on mobile phones.

  • eucMetric can be used instead of similar when you need different precision or want to sort by similarity. Example (not mine) of custom similarity function.

  • propMetric allows customization of image proportion threshold.

  • defaultThresholds prints default thresholds used in func similar and similar90270, as a starting point for selecting thresholds on eucMetric and propMetric.

Algorithm

Detailed explanation, also as a PDF.

Summary: Images are resized in a special way to squares of fixed size called "icons". Euclidean distance between the icons is used to give the similarity verdict. Also image proportions are used to avoid matching images of distinct shape.

Customization suggestions

To increase precision you can either use your own thresholds in func eucMetric (and propMetric) OR generate icons for image sub-regions and compare those icons.

To speedup file processing you may want to generate icons for available image thumbnails. Specifically, many JPEG images contain EXIF thumbnails, you could considerably speedup the reads by using decoded thumbnails to feed into func icon. External packages to read thumbnails: 1 and 2. A note of caution: in rare cases there could be issues with thumbnails not matching image content. EXIF standard specification: 1 and 2.