In this chapter, we will cover:
- Computing the image histogram
- Applying look-up tables to modify image appearance
- Equalizing the image histogram
- Backprojecting a histogram to detect specific image content
- Using the mean shift algorithm to find an object
- Retrieving similar images using histogram comparison
Computing the image histogram
Using the cv::calcHist function.
Caculate 1D Histogram
Histogram1D.h
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
class Histogram1D {
private:
int histSize[1]; // number of bins
float hranges[2]; // min and max pixel value
const float* ranges[1];
int channels[1]; // only 1 channel used here
public:
Histogram1D() {
// Prepare arguments for 1D histogram
histSize[0] = 256;
hranges[0] = 0.0;
hranges[1] = 255.0;
ranges[0] = hranges;
channels[0] = 0; // by default, we look at channel 0
}
// Computes the 1D histogram
cv::MatND getHistogram(const cv::Mat &image);
// Computes the 1D histogram and returns an image of it.
cv::MatND getHistogramImage(const cv::Mat &image);
};
Histogram.cpp
#include "Histgram1D.h"
// Computes the 1D histogram
cv::MatND Histogram1D::getHistogram(const cv::Mat &image) {
cv::MatND hist;
// Compute histogram
cv::calcHist(&image,
1, // histogram from 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
1, // it is a 1D histgram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
cv::Mat Histogram1D::getHistogramImage(const cv::Mat &image) {
// Compute histogram first
cv::MatND hist = getHistogram(image);
// Get min and max bin values
double maxVal = 0;
double minVal = 0;
cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);
// Image on which to display histogram
cv::Mat histImg(histSize[0], histSize[0], CV_8U, cv::Scalar(255));
// Set highest point at 90% of nbins
int hpt = static_cast <int >(0.9 * histSize[0]);
// Draw a vertical line for each bin
for ( int h = 0; h < histSize[0]; h++ ) {
float binVal = hist.at<float>(h);
int intensity = static_cast <int >(binVal * hpt / maxVal);
// This function draws a line between 2 points
cv::line(histImg, cv::Point(h, histSize[0]),
cv::Point(h, histSize[0] - intensity),
cv::Scalar::all(0));
}
return histImg;
}
main.cpp
#include <iostream>
#include "Histgram1D.h"
int main() {
// Read input image
cv::Mat image = cv::imread( "group.jpg", 0); // open in b&w
// The histogram object
Histogram1D h;
// Compute the histogram
cv::MatND histo = h.getHistogram(image);
// Loop over each bin
for (int i = 0; i < 256; i++) {
std::cout << "Value " << i << " = " <<
histo.at<float >(i) << std::endl;
}
// Draw histogram image
cv::Mat histoImage = h.getHistogramImage(image);
cv::namedWindow( "histogram", CV_WINDOW_AUTOSIZE);
cv::imshow( "histogram", histoImage);
// threshold the image
cv::Mat thresholded;
cv::threshold(image, thresholded, 60, 255, cv::THRESH_BINARY);
cv::namedWindow( "Binary image", CV_WINDOW_AUTOSIZE);
cv::imshow( "Binary image", thresholded);
cv::waitKey(0);
return 0;
}
the result as follows:
Caculate Color image histogram
ColoHistogram.h
#include <iostream>
#include "Histgram1D.h"
int main() {
// Read input image
cv::Mat image = cv::imread( "group.jpg", 0); // open in b&w
// The histogram object
Histogram1D h;
// Compute the histogram
cv::MatND histo = h.getHistogram(image);
// Loop over each bin
for (int i = 0; i < 256; i++) {
std::cout << "Value " << i << " = " <<
histo.at<float >(i) << std::endl;
}
// Draw histogram image
cv::Mat histoImage = h.getHistogramImage(image);
cv::namedWindow( "histogram", CV_WINDOW_AUTOSIZE);
cv::imshow( "histogram", histoImage);
// threshold the image
cv::Mat thresholded;
cv::threshold(image, thresholded, 60, 255, cv::THRESH_BINARY);
cv::namedWindow( "Binary image", CV_WINDOW_AUTOSIZE);
cv::imshow( "Binary image", thresholded);
cv::waitKey(0);
return 0;
}
ColorHistogram.cpp
#include "ColorHistogram.h"
// Computes the 1D histogram
cv::MatND ColorHistogram::getHistogram(const cv::Mat &image) {
cv::MatND hist;
// Compute histogram
cv::calcHist(&image,
1, // histogram from 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histgram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
cv::SparseMat ColorHistogram::getSpareHistogram(const cv::Mat &image) {
// Compute histogram first
cv::SparseMat hist(3, histSize, CV_32F);
// Compute histogram
cv::calcHist(&image,
1, // histogram from 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histgram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
Applying look-up tables to modify image appearance
A look-up table is a simple one-to-one (or many-to-one) function that defines how pixel values are transformed into new values. It is a 1D array with, in the case of regular gray-level images, 256 entries. Entry i of the table gives the new intensity value of the corresponding gray level, that is:
newIntensity= lookup[oldIntensity];
Function cv::LUT in OpenCV applies a look-up table to an image in order to produce a new image. We can add this function to our Histogram1D class:
cv::Mat Histogram1D::applyLookUp(const cv::Mat& image, // input image
const cv::Mat& lookup) { // 1*256 uchar matrix
// the output image
cv::Mat result;
// apply the lookup table
cv::LUT(image, lookup, result);
return result;
}
cv::Mat Histogram1D::strech(const cv::Mat &image, int minValue /* = 0 */) {
// Compute histogram first
cv::MatND hist = getHistogram(image);
// find left extremity of the histogram
int imin = 0;
for ( ; imin < histSize[0]; imin ++) {
std::cout << hist.at<float>(imin) << std::endl;
if (hist.at<float >(imin) > minValue) {
break;
}
}
// find right extremity of the histogram
int imax = histSize[0] - 1;
for ( ; imax >= 0; imax --) {
if (hist.at<float >(imax) > minValue)
break;
}
// Create lookup table
int dim(256);
cv::Mat lookup(1, // 1 dimension
&dim, // 256 entries
CV_8U // uchar
);
// Build lookup table
for (int i = 0; i < 256; i++) {
// stretch between imin and imax
if (i < imin) lookup.at<uchar>(i) = 0;
else if (i > imax) lookup.at<uchar>(i) = 255;
//linear mapping
else lookup.at<uchar>(i) = static_cast <uchar>(255.0 * (i - imin) / (imax - imin) + 0.5);
}
// Apply lookup table
cv::Mat result;
result = applyLookUp(image, lookup);
return result;
}
Using the function as follows:
cv::Mat streched = h.strech(image, 100);
cv::namedWindow( "streched image", CV_WINDOW_AUTOSIZE);
cv::imshow( "streched image", streched);
cv::Mat strechedHistoImage = h.getHistogramImage(streched);
cv::namedWindow( "strechedHistoImage", CV_WINDOW_AUTOSIZE);
cv::imshow( "strechedHistoImage", strechedHistoImage);
results as follows:
Equalizing the image histogram
OpenCV offers an easy-to-use function that performs histogram equalization. It can be called as follows:
cv::Mat Histogram1D::equalize(const cv::Mat &image) {
cv::Mat result;
cv::equalizeHist(image, result);
return result;
}
result as follows:
Backprojecting a histogram to detect specific image content
ContentFinder.h
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
class ContentFinder {
private:
float hranges[2];
const float* ranges[3];
int channels[3];
float threshold;
cv::MatND histogram;
public:
ContentFinder() : threshold(-1.0f) {
ranges[0] = hranges; // all channels have same range
ranges[1] = hranges;
ranges[2] = hranges;
}
// Sets the threshold on histogram values [0, 1]
void setThreshold(float t) {
threshold = t;
}
// Gets the threshold
float getThreshold() {
return threshold;
}
// Sets the reference histogram
void setHistogram(const cv::MatND &h) {
histogram = h;
cv::normalize(histogram, histogram, 1.0);
}
cv::Mat find(const cv::Mat &image, float minValue, float maxValue, int *channels, int dim);
};
ContentFinder.cpp
#include "ContentFinder.h"
cv::Mat ContentFinder::find(const cv::Mat &image,
float minValue,
float maxValue,
int *channels,
int dim) {
cv::Mat result;
hranges[0] = minValue;
hranges[1] = maxValue;
for (int i = 0; i < dim; i++) {
this->channels[i] = channels[i];
}
cv::calcBackProject(&image, 1, // input image
channels, // list of channels used
histogram, // the histogram we are using
result, // the resulting backprojection
ranges, // the range of values
255.0 // the scaling factor
);
// Threshold back projection to obtain a binary image
if (threshold > 0.0)
cv::threshold(result, result, 255 * threshold, 255, cv::THRESH_BINARY);
return result;
}
Let's now use a BGR histogram on the color version of the image we used above. This time, we will try to detect the blue sky area. We will first load the color image, reduce the number of color using the color reduction function of Chapter 2, and define the region of interest: ColorHistogram hc;
ColorHistogram hc;// load color imagecv::Mat color = cv::imread( "waves.jpg");//reduce colorscolor = hc.colorReduce(color, 32);// blue sky areacv::Mat imageROI = color(cv::Rect(0, 0, 165, 75));
Next, you compute the histogram and use the findmethod to detect the sky portion
of the image:
cv::MatND hist = hc.getHistogram(imageROI);ContentFinder finder;finder.setHistogram(hist);finder.setThreshold(0.05f);//Get back-projection of color histogramcv::Mat result = finder.find(color);cv::namedWindow( "original image", CV_WINDOW_AUTOSIZE);cv::imshow( "original image", color);cv::namedWindow( "color back projection result", CV_WINDOW_AUTOSIZE);cv::imshow( "color back projection result", result);
The result of the detection on the color version of the image, of the previous section is seen
here:


Using the mean shift algorithm to find an object
colorhistogram.h
#if !defined COLHISTOGRAM
#define COLHISTOGRAM
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
class ColorHistogram {
private:
int histSize[3];
float hranges[2];
const float* ranges[3];
int channels[3];
public:
ColorHistogram() {
// Prepare arguments for a color histogram
histSize[0]= histSize[1]= histSize[2]= 256;
hranges[0]= 0.0; // BRG range
hranges[1]= 255.0;
ranges[0]= hranges; // all channels have the same range
ranges[1]= hranges;
ranges[2]= hranges;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
}
// Computes the histogram.
cv::MatND getHistogram(const cv::Mat &image) {
cv::MatND hist;
// BGR color histogram
hranges[0]= 0.0; // BRG range
hranges[1]= 255.0;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
// Compute histogram
cv::calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
// Computes the histogram.
cv::SparseMat getSparseHistogram(const cv::Mat &image) {
cv::SparseMat hist(3,histSize,CV_32F);
// BGR color histogram
hranges[0]= 0.0; // BRG range
hranges[1]= 255.0;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
// Compute histogram
cv::calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
// Computes the 2D ab histogram.
// BGR source image is converted to Lab
cv::MatND getabHistogram(const cv::Mat &image) {
cv::MatND hist;
// Convert to Lab color space
cv::Mat lab;
cv::cvtColor(image, lab, CV_BGR2Lab);
// Prepare arguments for a 2D color histogram
hranges[0]= -128.0;
hranges[1]= 127.0;
channels[0]= 1; // the two channels used are ab
channels[1]= 2;
// Compute histogram
cv::calcHist(&lab,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
2, // it is a 2D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
// Computes the 1D Hue histogram with a mask.
// BGR source image is converted to HSV
cv::MatND getHueHistogram(const cv::Mat &image) {
cv::MatND hist;
// Convert to Lab color space
cv::Mat hue;
cv::cvtColor(image, hue, CV_BGR2HSV);
// Prepare arguments for a 1D hue histogram
hranges[0]= 0.0;
hranges[1]= 180.0;
channels[0]= 0; // the hue channel
// Compute histogram
cv::calcHist(&hue,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
1, // it is a 1D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
cv::Mat colorReduce(const cv::Mat &image, int div=64) {
int n= static_cast<int >(log(static_cast <double >(div))/log(2.0));
// mask used to round the pixel value
uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
cv::Mat_<cv::Vec3b>::const_iterator it= image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::const_iterator itend= image.end<cv::Vec3b>();
// Set output image (always 1-channel)
cv::Mat result(image.rows,image.cols,image.type());
cv::Mat_<cv::Vec3b>::iterator itr= result.begin<cv::Vec3b>();
for ( ; it!= itend; ++it, ++itr) {
(*itr)[0]= ((*it)[0]&mask) + div/2;
(*itr)[1]= ((*it)[1]&mask) + div/2;
(*itr)[2]= ((*it)[2]&mask) + div/2;
}
return result;
}
// Computes the 1D Hue histogram with a mask.
// BGR source image is converted to HSV
// Pixels with low saturation are ignored
cv::MatND getHueHistogram(const cv::Mat &image,
int minSaturation=0) {
cv::MatND hist;
// Convert to HSV color space
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
// Mask to be used (or not)
cv::Mat mask;
if (minSaturation>0) {
// Spliting the 3 channels into 3 images
std::vector<cv::Mat> v;
cv::split(hsv,v);
// Mask out the low saturated pixels
cv::threshold(v[1],mask,minSaturation,255,
cv::THRESH_BINARY);
}
// Prepare arguments for a 1D hue histogram
hranges[0]= 0.0;
hranges[1]= 180.0;
channels[0]= 0; // the hue channel
// Compute histogram
cv::calcHist(&hsv,
1, // histogram of 1 image only
channels, // the channel used
mask, // binary mask
hist, // the resulting histogram
1, // it is a 1D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
};
#endif
bojectFinder.h
#if !defined OFINDER
#define OFINDER
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
class ObjectFinder {
private:
float hranges[2];
const float* ranges[3];
int channels[3];
float threshold;
cv::MatND histogram;
cv::SparseMat shistogram;
bool isSparse;
public:
ObjectFinder() : threshold(0.1f), isSparse(false) {
ranges[0]= hranges; // all channels have the same range
ranges[1]= hranges;
ranges[2]= hranges;
}
// Sets the threshold on histogram values [0,1]
void setThreshold(float t) {
threshold= t;
}
// Gets the threshold
float getThreshold() {
return threshold;
}
// Sets the reference histogram
void setHistogram(const cv::MatND& h) {
isSparse= false;
histogram= h;
cv::normalize(histogram,histogram,1.0);
}
// Sets the reference histogram
void setHistogram(const cv::SparseMat& h) {
isSparse= true;
shistogram= h;
cv::normalize(shistogram,shistogram,1.0,cv::NORM_L2);
}
// Finds the pixels belonging to the histogram
cv::Mat find(const cv::Mat& image) {
cv::Mat result;
hranges[0]= 0.0; // range [0,255]
hranges[1]= 255.0;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
if (isSparse) { // call the right function based on histogram type
cv::calcBackProject(&image,
1, // one image
channels, // vector specifying what histogram dimensions belong to what image channels
shistogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
} else {
cv::calcBackProject(&image,
1, // one image
channels, // vector specifying what histogram dimensions belong to what image channels
histogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
}
// Threshold back projection to obtain a binary image
if (threshold>0.0)
cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);
return result;
}
cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels, int dim) {
cv::Mat result;
hranges[0]= minValue;
hranges[1]= maxValue;
for (int i=0; i<dim; i++)
this->channels[i]= channels[i];
if (isSparse) { // call the right function based on histogram type
cv::calcBackProject(&image,
1, // we only use one image at a time
channels, // vector specifying what histogram dimensions belong to what image channels
shistogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
} else {
cv::calcBackProject(&image,
1, // we only use one image at a time
channels, // vector specifying what histogram dimensions belong to what image channels
histogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
}
// Threshold back projection to obtain a binary image
if (threshold>0.0)
cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);
return result;
}
};
#endif
finder.cpp
#include <iostream>
#include <vector>
using namespace std;
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\video\tracking.hpp>
#include "objectFinder.h"
#include "colorhistogram.h"
int main()
{
// Read reference image
cv::Mat image= cv::imread("../baboon1.jpg" );
if (!image.data)
return 0;
// Define ROI
cv::Mat imageROI= image(cv::Rect(110,260,35,40));
cv::rectangle(image, cv::Rect(110,260,35,40),cv::Scalar(0,0,255));
// Display image
cv::namedWindow( "Image");
cv::imshow( "Image",image);
// Get the Hue histogram
int minSat=65;
ColorHistogram hc;
cv::MatND colorhist= hc.getHueHistogram(imageROI,minSat);
ObjectFinder finder;
finder.setHistogram(colorhist);
finder.setThreshold(0.2f);
// Convert to HSV space
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
// Split the image
vector<cv::Mat> v;
cv::split(hsv,v);
// Eliminate pixels with low saturation
cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
cv::namedWindow( "Saturation");
cv::imshow( "Saturation",v[1]);
// Get back-projection of hue histogram
int ch[1]={0};
cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::namedWindow( "Result Hue");
cv::imshow( "Result Hue",result);
cv::bitwise_and(result,v[1],result);
cv::namedWindow( "Result Hue and");
cv::imshow( "Result Hue and",result);
// Second image
image= cv::imread("../baboon3.jpg");
// Display image
cv::namedWindow( "Image 2");
cv::imshow( "Image 2",image);
// Convert to HSV space
cv::cvtColor(image, hsv, CV_BGR2HSV);
// Split the image
cv::split(hsv,v);
// Eliminate pixels with low saturation
cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
cv::namedWindow( "Saturation");
cv::imshow( "Saturation",v[1]);
// Get back-projection of hue histogram
result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::namedWindow( "Result Hue");
cv::imshow( "Result Hue",result);
// Eliminate low stauration pixels
cv::bitwise_and(result,v[1],result);
cv::namedWindow( "Result Hue and");
cv::imshow( "Result Hue and",result);
// Get back-projection of hue histogram
finder.setThreshold(-1.0f);
result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::bitwise_and(result,v[1],result);
cv::namedWindow( "Result Hue and raw");
cv::imshow( "Result Hue and raw",result);
cv::Rect rect(110,260,35,40);
cv::rectangle(image, rect, cv::Scalar(0,0,255));
cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;
cv::rectangle(image, rect, cv::Scalar(0,255,0));
// Display image
cv::namedWindow( "Image 2 result");
cv::imshow( "Image 2 result",image);
cv::waitKey();
return 0;
}
results:
Retrieving similar images using histogram comparison
imageComparator.h
#if !defined ICOMPARATOR
#define ICOMPARATOR
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include "colorhistogram.h"
class ImageComparator {
private:
cv::Mat reference;
cv::Mat input;
cv::MatND refH;
cv::MatND inputH;
ColorHistogram hist;
int div;
public:
ImageComparator() : div(32) {
}
// Color reduction factor
// The comparaison will be made on images with
// color space reduced by this factor in each dimension
void setColorReduction( int factor) {
div= factor;
}
int getColorReduction() {
return div;
}
void setReferenceImage(const cv::Mat& image) {
reference= hist.colorReduce(image,div);
refH= hist.getHistogram(reference);
}
double compare(const cv::Mat& image) {
input= hist.colorReduce(image,div);
inputH= hist.getHistogram(input);
return cv::compareHist(refH,inputH,CV_COMP_INTERSECT);
}
};
#endif
retrieve.cpp
#include <iostream>
using namespace std;
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include "imageComparator.h"
int main()
{
// Read reference image
cv::Mat image= cv::imread("../waves.jpg" );
if (!image.data)
return 0;
// Display image
cv::namedWindow( "Query Image");
cv::imshow( "Query Image",image);
ImageComparator c;
c.setReferenceImage(image);
// Read an image and compare it with reference
cv::Mat input= cv::imread("../dog.jpg" );
cout << "waves vs dog: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("../marais.jpg");
cout << "waves vs marais: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("../bear.jpg");
cout << "waves vs bear: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("../beach.jpg");
cout << "waves vs beach: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("../polar.jpg");
cout << "waves vs polar: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("../moose.jpg");
cout << "waves vs moose: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("../lake.jpg");
cout << "waves vs lake: " << c.compare(input) << endl;
// Read an image and compare it with reference
input= cv::imread("../fundy.jpg");
cout << "waves vs fundy: " << c.compare(input) << endl;
cv::waitKey();
return 0;
}
results: