Skip to content

Instantly share code, notes, and snippets.

@naezith
Created December 26, 2016 21:21
Show Gist options
  • Save naezith/438d04d193a279671a5a0dfe000361c6 to your computer and use it in GitHub Desktop.
Save naezith/438d04d193a279671a5a0dfe000361c6 to your computer and use it in GitHub Desktop.
Image Search with 64 Bin and Local Binary Pattern techniques
#include <opencv2/opencv.hpp>
#include <iostream>
const int IMG_COUNT = 50;
const std::string file_prefix = "Dataset/";
const std::string file_folder[2] = {"Color/", "Texture/"};
const std::string file_suffix = ".jpg";
typedef std::vector<int> HIST;
typedef std::pair<int, double> DIST;
void getInputs(int &type, int &image_id) {
// Get search type
do {
system("CLS");
std::cout << "// Image search \n 1-) Color \n 2-) Texture \n>> ";
std::cin >> type;
} while(type != 1 && type != 2);
--type;
// Get the input file
do {
std::cout << "// Enter the image ID (1.." << IMG_COUNT << ") \n>> ";
std::cin >> image_id;
} while(image_id < 1 || image_id > IMG_COUNT);
--image_id;
}
// Get euclidean distance of histograms
double calcDistance(HIST &a, HIST &b){
unsigned long long sum = 0;
for(unsigned i = 0; i < a.size(); ++i) {
int diff = a[i] - b[i];
sum += diff*diff;
}
return sqrt(sum);
}
// Fill histogram with R2G2B2
void calc64BinHist(const cv::Mat &img, HIST &hist) {
for (int i = 0; i < img.rows; ++i) {
for (int j = 0; j < img.cols; ++j) {
const cv::Vec3b &curr = img.at<cv::Vec3b>(i, j);
// Create a 6 bit number with first two bits of each color channel, R G B
++hist[((curr[2] & 0b11000000) >> 2) | ((curr[1] & 0b11000000) >> 4) | ((curr[0] & 0b11000000) >> 6)];
}
}
}
#include <unordered_map>
void calcLBPHist(const cv::Mat &img, HIST &hist) {
cv::Mat gray;
cvtColor(img, gray, cv::COLOR_BGR2GRAY);
std::unordered_map<unsigned char, unsigned char> idx; // code's slot 0..255 -> 0..58
for (int i = 1; i < gray.rows - 1; ++i) {
for (int j = 1; j < gray.cols - 1; ++j) {
const unsigned char &curr = gray.at<unsigned char>(i, j);
// Have the code as boolean array to count for switches later
bool code_bool[8] = {
gray.at<unsigned char>(i - 1, j - 1) >= curr,
gray.at<unsigned char>(i - 1, j ) >= curr,
gray.at<unsigned char>(i - 1, j + 1) >= curr,
gray.at<unsigned char>(i, j + 1) >= curr,
gray.at<unsigned char>(i + 1, j + 1) >= curr,
gray.at<unsigned char>(i + 1, j ) >= curr,
gray.at<unsigned char>(i + 1, j - 1) >= curr,
gray.at<unsigned char>(i, j - 1) >= curr
};
// Count switches and turn the boolean array into unsigned char
unsigned char code = 0; // The character representation of our code
unsigned char switch_count = 0; // 0-1 or 1-0 Switch count
{
unsigned char prev_bit; // Previous bit, to count switches
for(int b = 0; b < 8; ++b) {
unsigned char bit = code_bool[b];
if(b > 0 && bit != prev_bit) ++switch_count;
prev_bit = bit;
code |= bit << (7 - b);
}
}
if(switch_count > 2) ++hist[58]; // Fill all the >2 ones in one bin
else{ // If we did not index this code yet, then give it an index in hist[59] array
if(!idx.count(code)) {
unsigned char new_bin = idx.size();
idx[code] = new_bin; // New bin is the current count of given indexes
}
// Increment the histogram bin by one
++hist[idx[code]];
}
}
}
}
int main() {
// Load images
std::cout << "Loading database..." << std::endl;
cv::Mat images[2][IMG_COUNT];
for(int t = 0; t < 2; ++t) {
for(int k = 0; k < IMG_COUNT; ++k) {
const std::string file_name = file_prefix + file_folder[t] + std::to_string(k + 1) + file_suffix;
cv::Mat &img = images[t][k];
img = cv::imread(file_name, 1);
if(!img.data) { std::cout << "Can't open file " << file_name << std::endl; return -1; }
}
}
// Calculate 64 bin histograms and LBP histograms
HIST BIN64_hists[IMG_COUNT], LBP_hists[IMG_COUNT];
for(int k = 0; k < IMG_COUNT; ++k) {
BIN64_hists[k].resize(64, 0);
calc64BinHist(images[0][k], BIN64_hists[k]);
LBP_hists[k].resize(59, 0);
calcLBPHist(images[1][k], LBP_hists[k]);
}
// Get user input to find closest images
while(1){
// Get Inputs
int type, image_id;
getInputs(type, image_id);
// Calculate distances
std::vector<DIST> distance(IMG_COUNT);
{
HIST* chosen_hists = type == 0 ? BIN64_hists : LBP_hists;
for(unsigned k = 0; k < distance.size(); ++k) {
distance[k].first = k;
distance[k].second = calcDistance(chosen_hists[image_id], chosen_hists[k]);
}
// Sort distances
std::sort(distance.begin(), distance.end(), [] (const DIST& a, const DIST& b) { return a.second < b.second; });
}
// Show the best matches
{
// Show the input image
cv::namedWindow("Input", cv::WINDOW_AUTOSIZE);
cv::imshow("Input", images[type][image_id]);
// Show matches
int shown = 0;
for(int k = 0; k < IMG_COUNT && shown < 5; ++k) {
int id = distance[k].first;
if(id != image_id) {
std::string window_name = std::to_string(++shown);
std::cout << " #" << window_name << " -> ID:" << (id + 1) << " -> " << distance[k].second << std::endl;
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
cv::imshow(window_name, images[type][id]);
}
}
}
// Wait until the presses any key
cv::waitKey(0);
cv::destroyAllWindows();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment