Created
December 26, 2016 21:21
-
-
Save naezith/438d04d193a279671a5a0dfe000361c6 to your computer and use it in GitHub Desktop.
Image Search with 64 Bin and Local Binary Pattern techniques
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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