Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

How to build a fuzzy image database on top of OpenCV?

I'm learning this topic for last 6 months but still unable to do it.

I know that in theory i have to calculate features for all images, and then store them into Bag of words or something. When it's done I can use FLANN or something to find nearest image in the dataset, and if delta between features is small then we consider it's to be a match, otherwise a new image goes to the database.

My question is how it may be implemented in practice? I tried really hard, but C++ code is complicated enough (e.g. I don't understand mat.push_back method. Is it just a 2d array? Or it's a vec of something? It's really confusing to me).

I'd appreciate any help or links, especially if it would be some practical explanation

How to build a fuzzy image database on top of OpenCV?

I'm learning this topic for last 6 months but still unable to do it.

I know that in theory i have to calculate features for all images, and then store them into Bag of words or something. When it's done I can use FLANN or something to find nearest image in the dataset, and if delta between features is small then we consider it's to be a match, otherwise a new image goes to the database.

My question is how it may be implemented in practice? I tried really hard, but C++ code is complicated enough (e.g. I don't understand mat.push_back method. Is it just a 2d array? Or it's a vec of something? It's really confusing to me).practice?

I'd appreciate any help or links, especially if it would be some practical explanation

How to build a fuzzy image database on top of OpenCV?

I'm learning this topic for last 6 months but still unable to do it.

I know that in theory i have to calculate features for all images, and then store them into Bag of words or something. When it's done I can use FLANN or something to find nearest image in the dataset, and if delta between features is small then we consider it's to be a match, otherwise a new image goes to the database.

My question is how it may be implemented in practice?

I'd appreciate any help or links, especially if it would be some practical explanation

How to build a fuzzy image database on top of OpenCV?train BoW incrementaly?

I'm learning this topic I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for last 6 months but still unable to do it.

I know that an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in theory i have to a nutshell: I have a stream of images. I can calculate features for all images, and then store them into Bag of words or something. When it's done I can use FLANN or something to find nearest image of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in the dataset, and if delta between features is small then we consider it's to be a match, otherwise a new image goes to the database.

My question is how it may be implemented in practice?

I'd appreciate any help or links, especially if it would be some practical explanationfuture I say "existing".

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. This task is done by all search engines on the internet so I'm surprised there is almost no information in the web.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (no images). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. This task is done by all search engines on the internet so I'm surprised there is almost no information in the web.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (no images). (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. This task is done by all search engines on the internet so I'm surprised there is almost no information in the web.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. This task is done by all search engines on the internet so I'm surprised there is almost no information in the web.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. This task is done by all search engines on the internet so I'm surprised there is almost no information in the web.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. This task is done by all All search engines on the internet are doing this so I'm surprised there is almost no information in the web.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. All search engines on the internet are doing this so I'm surprised there is almost no information in the web.


Okay, I tried to use PHash to perform a search operation. But it fails to recognize slightly different images. here is a sample code (thin rust wrapper over C++ interface, so don't be affraid to se an alien language):

pub trait Database {
    fn save_image(&mut self, image: &[u8]);
    fn load_images(&self) -> Vec<Vec<u8>>;
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ImageVariant<D: Clone> {
    New,
    AlreadyExists(D)
}

pub struct Storage<T: Database, D: Clone> {
    database: T,
    hasher: PHash,
    images: Vec<(Mat, D)>
}

impl<T: Database, D: Clone> Storage<T, D> {
    pub fn new(database: T) -> Self {
        Self {
            database,
            hasher: PHash::new(),
            images: Vec::new()
        }
    }
}

// the main execution happens here
impl<T: Database, D: Clone> Storage<T, D> {
    pub fn save_image_if_new(&mut self, image: &[u8], data: D) -> ImageVariant<D> {
        const DIFF: f64 = 0.5;

        let mat = Mat::image_decode(image, ImageReadMode::Grayscale);
        let mat = self.hasher.compute(&mat);
        let mut last_diff = std::f64::INFINITY;
        let mut result: Option<D> = None;
        for &(ref image, ref d) in self.images.iter() {
            let diff = self.hasher.compare(&mat, &image);
            if diff < last_diff {
                last_diff = diff;
                result = Some(d.clone());
            }
        }
        if last_diff < DIFF {
            return ImageVariant::AlreadyExists(result.unwrap());
        }
        self.database.save_image(image);
        self.images.push((mat, data));
        ImageVariant::New
    }
}

Then I run the test on following images:

  1. image description
  2. image description
  3. image description

So PHash.compare gives distance 28 for images 1 and 2 (should get small distance), while it gives 30 for images 1 and 3 (should get a big distance). So it's practically doesn't allow to know if I already say this image before.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. All search engines on the internet are doing this so I'm surprised there is almost no information in the web.


Okay, I tried to use PHash to perform a search operation. But it fails to recognize slightly different images. here is a sample code (thin rust (it's a thin Rust wrapper over C++ interface, so don't be affraid to se see an alien language):

pub trait Database {
    fn save_image(&mut self, image: &[u8]);
    fn load_images(&self) -> Vec<Vec<u8>>;
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ImageVariant<D: Clone> {
    New,
    AlreadyExists(D)
}

pub struct Storage<T: Database, D: Clone> {
    database: T,
    hasher: PHash,
    images: Vec<(Mat, D)>
}

impl<T: Database, D: Clone> Storage<T, D> {
    pub fn new(database: T) -> Self {
        Self {
            database,
            hasher: PHash::new(),
            images: Vec::new()
        }
    }
}

// the main execution happens here
impl<T: Database, D: Clone> Storage<T, D> {
    pub fn save_image_if_new(&mut self, image: &[u8], data: D) -> ImageVariant<D> {
        const DIFF: f64 = 0.5;

        let mat = Mat::image_decode(image, ImageReadMode::Grayscale);
        let mat = self.hasher.compute(&mat);
        let mut last_diff = std::f64::INFINITY;
        let mut result: Option<D> = None;
        for &(ref image, ref d) in self.images.iter() {
            let diff = self.hasher.compare(&mat, &image);
            if diff < last_diff {
                last_diff = diff;
                result = Some(d.clone());
            }
        }
        if last_diff < DIFF {
            return ImageVariant::AlreadyExists(result.unwrap());
        }
        self.database.save_image(image);
        self.images.push((mat, data));
        ImageVariant::New
    }
}

Then I run the test on following images:

  1. image description
  2. image description
  3. image description

So PHash.compare gives distance 28 for images 1 and 2 (should get small distance), while it gives 30 for images 1 and 3 (should get a big distance). So it's practically doesn't allow to know if I already say this image before.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. All search engines on the internet are doing this so I'm surprised there is almost no information in the web.


Okay, I tried to use PHash to perform a search operation. But it fails to recognize slightly different images. here is a sample code (it's a thin Rust wrapper over C++ interface, so don't be affraid to see an alien language):

pub trait Database {
    fn save_image(&mut self, image: &[u8]);
    fn load_images(&self) -> Vec<Vec<u8>>;
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ImageVariant<D: Clone> ImageVariant {
    New,
    AlreadyExists(D)
AlreadyExists
}

pub struct Storage<T: Database, D: Clone> Database> {
    database: T,
    hasher: PHash,
    images: Vec<(Mat, D)>
Vec<Mat>
}

impl<T: Database, D: Clone> Storage<T, D> Database> Storage<T> {
    pub fn new(database: T) -> Self {
        Self {
            database,
            hasher: PHash::new(),
            images: Vec::new()
        }
    }
}

// the main execution happens here
impl<T: Database, D: Clone> Storage<T, D> {
    pub fn save_image_if_new(&mut self, image: &[u8], data: D) filename: &str) -> ImageVariant<D> ImageVariant {
        const DIFF: f64 = 0.5;

        let mat = Mat::image_decode(image, ImageReadMode::Grayscale);
        let mat = self.hasher.compute(&mat);
        let mut last_diff = std::f64::INFINITY;
        let mut result: Option<D> = None;
        for &(ref image, ref d) image in self.images.iter() {
            let diff = self.hasher.compare(&mat, &image);
            if diff < last_diff {
                last_diff = diff;
                result = Some(d.clone());
            }
        }
        if last_diff < DIFF {
            return ImageVariant::AlreadyExists(result.unwrap());
ImageVariant::AlreadyExists;
        }
        self.database.save_image(image);
        self.images.push((mat, data));
self.images.push(mat);
        ImageVariant::New
    }
}

Then I run the test on following images:

  1. image description
  2. image description
  3. image description

So PHash.compare gives distance 28 for images 1 and 2 (should get small distance), while it gives 30 for images 1 and 3 (should get a big distance). So it's practically doesn't allow to know if I already say this image before.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. All search engines on the internet are doing this so I'm surprised there is almost no information in the web.


Okay, I tried to use PHash to perform a search operation. But it fails to recognize slightly different images. here is a sample code (it's a thin Rust wrapper over C++ interface, so don't be affraid to see an alien language):

pub trait Database {
    fn save_image(&mut self, image: &[u8]);
    fn load_images(&self) -> Vec<Vec<u8>>;
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ImageVariant {
    New,
    AlreadyExists
}

pub struct Storage<T: Database> {
    database: T,
    hasher: PHash,
    images: Vec<Mat>
}

impl<T: Database> Storage<T> {
    pub fn new(database: T) -> Self {
        Self {
            database,
            hasher: PHash::new(),
            images: Vec::new()
        }
    }
}

// the main execution happens here
impl<T: Database, D: Clone> Storage<T, D> {
    pub fn save_image_if_new(&mut self, image: &[u8], filename: &str) -> ImageVariant {
        const DIFF: f64 = 0.5;

        let mat = Mat::image_decode(image, ImageReadMode::Grayscale);
        let mat = self.hasher.compute(&mat);
        let mut last_diff = std::f64::INFINITY;
        for image in self.images.iter() {
            let diff = self.hasher.compare(&mat, &image);
            if diff < last_diff {
                last_diff = diff;
            }
        }
        if last_diff < DIFF {
            return ImageVariant::AlreadyExists;
        }
        self.database.save_image(image);
        self.images.push(mat);
        ImageVariant::New
    }
}

Then I run the test on following images:

  1. image description
  2. image description
  3. image description

So PHash.compare gives distance 28 for images 1 and 2 (should get small distance), while it gives 30 for images 1 and 3 (should get a big distance). So it's practically doesn't allow to know if I already say this image before.

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. All search engines on the internet are doing this so I'm surprised there is almost no information in the web.


Okay, I tried to use PHash to perform a search operation. But it fails to recognize slightly different images. here is a sample code (it's a thin Rust wrapper over C++ interface, so don't be affraid to see an alien language):

pub trait Database {
    fn save_image(&mut self, image: &[u8]);
    fn load_images(&self) -> Vec<Vec<u8>>;
}

pub enum ImageVariant {
    New,
    AlreadyExists
}

pub struct Storage<T: Database> {
    database: T,
    hasher: PHash,
    images: Vec<Mat>
}

impl<T: Database> Storage<T> {
    pub fn new(database: T) -> Self {
        Self {
            database,
            hasher: PHash::new(),
            images: Vec::new()
        }
    }
}

// the main execution happens here
impl<T: Database, D: Clone> Storage<T, D> Storage<T> {
    pub fn save_image_if_new(&mut self, image: &[u8], filename: &str) -> ImageVariant {
        const DIFF: f64 = 0.5;

        let mat = Mat::image_decode(image, ImageReadMode::Grayscale);
        let mat = self.hasher.compute(&mat);
        let mut last_diff = std::f64::INFINITY;
        for image in self.images.iter() {
            let diff = self.hasher.compare(&mat, &image);
            if diff < last_diff {
                last_diff = diff;
            }
        }
        if last_diff < DIFF {
            return ImageVariant::AlreadyExists;
        }
        self.database.save_image(image);
        self.images.push(mat);
        ImageVariant::New
    }
}

Then I run the test on following images:

  1. image description
  2. image description
  3. image description

So PHash.compare gives distance 28 for images 1 and 2 (should get small distance), while it gives 30 for images 1 and 3 (should get a big distance). So it's practically doesn't allow to know if I already say this image before.

The entire code is available here and here

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. All search engines on the internet are doing this so I'm surprised there is almost no information in the web.


Okay, I tried to use PHash to perform a search operation. But it fails to recognize slightly different images. here is a sample code (it's a thin Rust wrapper over C++ interface, so don't be affraid to see an alien language):

pub trait Database {
    fn save_image(&mut self, image: &[u8]);
    fn load_images(&self) -> Vec<Vec<u8>>;
}

pub enum ImageVariant {
    New,
    AlreadyExists
}

pub struct Storage<T: Database> {
    database: T,
    hasher: PHash,
    images: Vec<Mat>
}

impl<T: Database> Storage<T> {
    pub fn new(database: T) -> Self {
        Self {
            database,
            hasher: PHash::new(),
            images: Vec::new()
        }
    }
}

// the main execution happens here
impl<T: Database, D: Clone> Storage<T> {
    pub fn save_image_if_new(&mut self, image: &[u8], filename: &str) -> ImageVariant {
        const DIFF: f64 = 0.5;

        let mat = Mat::image_decode(image, ImageReadMode::Grayscale);
        let mat = self.hasher.compute(&mat);
        let mut last_diff = std::f64::INFINITY;
        for image in self.images.iter() {
            let diff = self.hasher.compare(&mat, &image);
            if diff < last_diff {
                last_diff = diff;
            }
        }
        if last_diff < DIFF {
            return ImageVariant::AlreadyExists;
        }
        self.database.save_image(image);
        self.images.push(mat);
        ImageVariant::New
    }
}

Then I run the test on following images:

  1. image description
  2. image description
  3. image description

So PHash.compare gives distance 28 for images 1 and 2 (should get small distance), while it gives 30 for images 1 and 3 (should get a big distance). So it's practically doesn't allow to know if I already say this image before.

The entire code is available here and here

Test code:

#[test]
fn it_works() {
    let lenna = fs::read(get_asset_path("lenna.png")).unwrap();
    let lenna_demotivator = fs::read(get_asset_path("lenna_demotivator.png")).unwrap();
    let solvay_conference = fs::read(get_asset_path("Solvay_conference_1927.jpg")).unwrap();
    let db = imagedb::InMemoryDatabase::new();
    let mut storage = imagedb::Storage::new(db);
    let result = storage.save_image_if_new(&lenna, "lenna");
    let result_demotivator = storage.save_image_if_new(&lenna_demotivator, "lenna demotivator");
    let result_solvay_conference = storage.save_image_if_new(&solvay_conference, "solvay_conference");
    assert_eq!(result, imagedb::ImageVariant::New);
    assert_ne!(result_demotivator, imagedb::ImageVariant::New);
    assert_eq!(result_solvay_conference, imagedb::ImageVariant::New);
}

How to train BoW incrementaly?

I want to implement an image DB. If i've got it correctly, I need to use BoW and then I would be able to classify a new image if I have seen it before.

But all examples I have seen nefore (e.g. this) have some set of traning images, when training is done then BoW is using to classify the rest.

My situation is different: I have to search through the BoW for an image, and if there is no match then we consider it to be a new image, otherwise we don't add it and say "already exist". Skewed, rescaled and others images should be considered the same, so I used SIFT as a feature detector.

So the problem in a nutshell: At beginning, I have an empty database of images (so I have no data to train on). I have a stream of images. I can calculate features of this image. Then I have check if there is some images which descriptors match descriptors of this image more than some treshold then I say "existing", otherwise I add these descriptors as a new image and return "new image". If I get the same image (or similar image) in future I say "existing".

It requires rebuild BoW with every new image, which seems to be a very complicated solution. All search engines on the internet are doing this so I'm surprised there is almost no information in the web.


Okay, I tried to use PHash to perform a search operation. But it fails to recognize slightly different images. here is a sample code (it's a thin Rust wrapper over C++ interface, so don't be affraid to see an alien language):

pub trait Database {
    fn save_image(&mut self, image: &[u8]);
    fn load_images(&self) -> Vec<Vec<u8>>;
}

pub enum ImageVariant {
    New,
    AlreadyExists
}

pub struct Storage<T: Database> {
    database: T,
    hasher: PHash,
    images: Vec<Mat>
}

impl<T: Database> Storage<T> {
    pub fn new(database: T) -> Self {
        Self {
            database,
            hasher: PHash::new(),
            images: Vec::new()
        }
    }
}

// the main execution happens here
impl<T: Database, D: Clone> Storage<T> {
    pub fn save_image_if_new(&mut self, image: &[u8], filename: &str) -> ImageVariant {
        const DIFF: f64 = 0.5;

        let mat = Mat::image_decode(image, ImageReadMode::Grayscale);
        let mat = self.hasher.compute(&mat);
        let mut last_diff = std::f64::INFINITY;
        for image in self.images.iter() {
            let diff = self.hasher.compare(&mat, &image);
            if diff < last_diff {
                last_diff = diff;
            }
        }
        if last_diff < DIFF {
            return ImageVariant::AlreadyExists;
        }
        self.database.save_image(image);
        self.images.push(mat);
        ImageVariant::New
    }
}

Then I run the test on following images:

  1. image description
  2. image description
  3. image description

So PHash.compare gives distance 28 for images 1 and 2 (should get small distance), while it gives 30 for images 1 and 3 (should get a big distance). So it's practically doesn't allow to know if I already say this image before.

The entire code is available here and here

Test code:

#[test]
fn it_works() {
    let lenna = fs::read(get_asset_path("lenna.png")).unwrap();
    let lenna_demotivator = fs::read(get_asset_path("lenna_demotivator.png")).unwrap();
    let solvay_conference = fs::read(get_asset_path("Solvay_conference_1927.jpg")).unwrap();
    let db = imagedb::InMemoryDatabase::new();
    let mut storage = imagedb::Storage::new(db);
    let result = storage.save_image_if_new(&lenna, "lenna");
    let result_demotivator = storage.save_image_if_new(&lenna_demotivator, "lenna demotivator");
    let result_solvay_conference = storage.save_image_if_new(&solvay_conference, "solvay_conference");
    assert_eq!(result, imagedb::ImageVariant::New);
    assert_ne!(result_demotivator, imagedb::ImageVariant::New);
    assert_eq!(result_solvay_conference, imagedb::ImageVariant::New);
}