How increase the contrast of an image with opencv c++? - c++

I want to increase the contrast of the bellow picture, with opencv c++.
I use histogram processing techniques e.g., histogram equalization (HE), histogram specification, etc. But I don't reaches to good result such as bellow images:
What ideas on how to solve this task would you suggest? Or on what resource on the internet can I find help?

I found a useful subject on OpenCV for changing image contrast :
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace cv;
double alpha; /**< Simple contrast control */
int beta; /**< Simple brightness control */
int main( int argc, char** argv )
{
/// Read image given by user
Mat image = imread( argv[1] );
Mat new_image = Mat::zeros( image.size(), image.type() );
/// Initialize values
std::cout<<" Basic Linear Transforms "<<std::endl;
std::cout<<"-------------------------"<<std::endl;
std::cout<<"* Enter the alpha value [1.0-3.0]: ";std::cin>>alpha;
std::cout<<"* Enter the beta value [0-100]: "; std::cin>>beta;
/// Do the operation new_image(i,j) = alpha*image(i,j) + beta
for( int y = 0; y < image.rows; y++ )
{ for( int x = 0; x < image.cols; x++ )
{ for( int c = 0; c < 3; c++ )
{
new_image.at<Vec3b>(y,x)[c] =
saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
}
}
}
/// Create Windows
namedWindow("Original Image", 1);
namedWindow("New Image", 1);
/// Show stuff
imshow("Original Image", image);
imshow("New Image", new_image);
/// Wait until user press some key
waitKey();
return 0;
}
See: Changing the contrast and brightness of an image!

I'm no expert but you could try to reduce the number of colours by merging grays into darker grays, and light grays into whites.
E.g.:
Find the least common colour in <0.0, 0.5) range, merge it towards black.
Find the least common colour in <0.5, 1.0> range, merge it towards white.
This would reduce the number of colours and help create a gap between brigher darker colours maybe.

This might be late, but you can try createCLAHE() function in openCV. Works fine for me.

Related

use warpAffine of OpenCV to do image registration

I am trying to do an image registration with ORB feature.
I got a problem at using warpAffine. The compiler told that it is not possible to convert parameter '1' from cv::Mat * to cv::InputArray.
Here is my code:
#pragma once
// Standard C++ I/O library.
#include <iostream>
#include <string>
#include <iomanip>
#include <vector>
// OpenCV library.
#include <cv.h>
#include <highgui.h>
// OpenCV feature library.
#include <opencv2/opencv.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <nonfree/features2d.hpp>
// main().
int main(int argv, char ** argc)
{
cv::Mat im_ref, im_cmp;
std::string str_ref, str_cmp;
// Read reference image.
//std::cout<<"Input reference image filename: ";
//std::cin>>str_ref;
std::cout<<"-> Reading images."<<std::endl;
str_ref = "F:\\CPPs\\ImageRegistration\\OpenCVTest\\206.png";
im_ref = cv::imread(str_ref);
cv::imshow("Reference image", im_ref);
// Read testing image.
//std::cout<<"Input testing image filename: ";
//std::cin>>str_cmp;
str_cmp = "F:\\CPPs\\ImageRegistration\\OpenCVTest\\227.png";
im_cmp = cv::imread(str_cmp);
cv::imshow("Testing image", im_cmp);
std::cout<<"Press any key to continue."<<std::endl;
cvWaitKey(0);
// Feature detection.
std::cout<<"-> Feature detection."<<std::endl;
std::vector <cv::KeyPoint> key_ref, key_cmp; // Vectors for features extracted from reference and testing images.
cv::Mat des_ref, des_cmp; // Descriptors for features of 2 images.
cv::ORB orb1; // An ORB object.
orb1(im_ref, cv::Mat(), key_ref, des_ref); // Feature extraction.
orb1(im_cmp, cv::Mat(), key_cmp, des_cmp);
// Show keypoints.
std::cout<<"-> Show keypoints."<<std::endl;
cv::Mat drawkey_ref, drawkey_cmp; // Output image for keypoint drawing.
cv::drawKeypoints(im_ref, key_ref, drawkey_ref); // Generate image for keypoint drawing.
cv::imshow("Keypoints of reference", drawkey_ref);
cv::drawKeypoints(im_cmp, key_cmp, drawkey_cmp);
cv::imshow("Keypoints of test", drawkey_cmp);
cvWaitKey(0);
// Matching.
std::cout<<"-> Matching."<<std::endl;
cv::FlannBasedMatcher matcher1(new cv::flann::LshIndexParams(20,10,2));
std::vector<cv::DMatch> matches1;
matcher1.match(des_ref, des_cmp, matches1); // Match two sets of features.
double max_dist = 0;
double min_dist = 100;
// Find out the minimum and maximum of all distance.
for( int i = 0; i < des_ref.rows; i++ )
{
double dist = matches1[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
cvWaitKey(0);
// Eliminate relatively bad points.
std::cout<<"-> Bad points elimination"<<std::endl;
std::vector<cv::KeyPoint> kgood_ref, kgood_cmp;
std::vector<cv::DMatch> goodMatch;
for (int i=0; i<matches1.size(); i++)
{
if(matches1[i].distance < 2*min_dist) // Keep points that are less than 2 times of the minimum distance.
{
goodMatch.push_back(matches1[i]);
kgood_ref.push_back(key_ref[i]);
kgood_cmp.push_back(key_cmp[i]);
} // end if
} // end for
cvWaitKey(0);
// Calculate affine transform matrix.
std::cout<<"-> Calculating affine transformation."<<std::endl;
std::vector<cv::Point2f> frm1_feature, frm2_feature;
const int p_size = goodMatch.size();
// * tmpP = new tmpPoint[p_size];
cv::Point2f tmpP;
for(int i=0; i<goodMatch.size(); i++)
{
tmpP.x = kgood_ref[i].pt.x;
tmpP.y = kgood_ref[i].pt.y;
frm1_feature.push_back(tmpP);
tmpP.x = kgood_cmp[i].pt.x;
tmpP.y = kgood_cmp[i].pt.y;
frm2_feature.push_back(tmpP);
}
cv::Mat affine_mat = cv::estimateRigidTransform(frm1_feature, frm2_feature, true);
cv::Mat im_transformed;
// Output results.
cv::warpAffine(&im_cmp, &im_transformed, affine_mat, CV_INTER_LINEAR|CV_WARP_FILL_OUTLIERS); // error comes from here.
cv::imshow("Transformed image", im_transformed);
cvWaitKey(0);
return 0;
}
I have got the result before using the answer given by Evgeniy.
The transform I had used is
//cv::warpAffine( im_cmp, im_transformed, affine_mat, cv::Size(im_cmp.cols, im_cmp.rows) );
The transformed result is quite strange
What I want to do is finally get a merged image of both the reference image and this transformed image. This is actually my first step. Is this the problem of using the transformation parameter of the warpAffine().
Finally, I want to get a result like an example here (two images taken at difference position and they are finally aligned)
You are giving a pointer, but wrapAffine accepts reference to a cv::Mat.
You can change your code like this:
cv::warpAffine(im_cmp, im_transformed, affine_mat, cv::Size(), CV_INTER_LINEAR|CV_WARP_FILL_OUTLIERS);
Just remove '&'

How to prepare image data for kmeans opencv function input? [duplicate]

I am making a function using C++ and OpenCV that will detect the color of a pixel in an image, determine what color range it is in, and replace it with a generic color. For example, green could range from dark green to light green, the program would determine that its still green and replace it with a simple green, making the output image very simple looking. everything is set up but I'm having trouble defining the characteristics of each range and was curious if anyone knows or a formula that, given BGR values, could determine the overall color of a pixel. If not I'll have to do much experimentation and make it myself, but if something already exists that'd save time. I've done plenty of research and haven't found anything so far.
If you want to make your image simpler (i.e. with less colors), but good looking, you have a few options:
A simple approach would be to divide (integer division) by a factor N the image, and then multiply by a factor N.
Or you can divide your image into K colors, using some clustering algorithm such as kmeans showed here, or median-cut algorithm.
Original image:
Reduced colors (quantized, N = 64):
Reduced colors (clustered, K = 8):
Code Quantization:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
uchar N = 64;
img /= N;
img *= N;
imshow("Reduced", img);
waitKey();
return 0;
}
Code kmeans:
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
imshow("Original", img);
// Cluster
int K = 8;
int n = img.rows * img.cols;
Mat data = img.reshape(1, n);
data.convertTo(data, CV_32F);
vector<int> labels;
Mat1f colors;
kmeans(data, K, labels, cv::TermCriteria(), 1, cv::KMEANS_PP_CENTERS, colors);
for (int i = 0; i < n; ++i)
{
data.at<float>(i, 0) = colors(labels[i], 0);
data.at<float>(i, 1) = colors(labels[i], 1);
data.at<float>(i, 2) = colors(labels[i], 2);
}
Mat reduced = data.reshape(3, img.rows);
reduced.convertTo(reduced, CV_8U);
imshow("Reduced", reduced);
waitKey();
return 0;
}
Yes, what you probably mean by "Overall color of a pixel" is either the "Hue" or "Saturation" of the color.
So you want a formula that transform RGB to HSV (Hue, Saturation, Value), and then you would only be interested by the Hue or Saturation values.
See: Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both
EDIT: You might need to max out the saturation, and then convert it back to RGB, and inspect which value is the highest (for instance (255,0,0), or (255,0,255), etc.
If you want to access RGB value of all pixels , then below is code,
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat image = imread("image_path");
for(int row = 1; row < image.rows; row++)
{
for(int col = 1; col < image.cols; col++)
{
Vec3b rgb = image.at<Vec3b>(row, col);
}
}
}

opencv - shrink objects to pixels

I am processing such an image as shown in Fig.1, which is composed of an array of points and required to convert to Fig. 2.
Fig.1 original image
Fig.2 wanted image
In order to finish the conversion, firstly I detect the edge of every point and then operate dilation. The result is satisfactory after choosing the proper parameters, seen in Fig. 3.
Fig.3 image after dilation
I processed the same image before in MATLAB. When it comes to shrink objects (in Fig.3) to pixels, function bwmorph(Img,'shrink',Inf) works and the result is exactly where Fig. 2 comes from. So how to get the same wanted image in opencv? It seems that there is no similar shrink function.
Here is my code of finding edge and dilation operation:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
using namespace cv;
// Global variables
Mat src, dilation_dst;
int dilation_size = 2;
int main(int argc, char *argv[])
{
IplImage* img = cvLoadImage("c:\\001a.bmp", 0); // 001a.bmp is Fig.1
// Perform canny edge detection
cvCanny(img, img, 33, 100, 3);
// IplImage to Mat
Mat imgMat(img);
src = img;
// Create windows
namedWindow("Dilation Demo", CV_WINDOW_AUTOSIZE);
Mat element = getStructuringElement(2, // dilation_type = MORPH_ELLIPSE
Size(2*dilation_size + 1, 2*dilation_size + 1),
Point(dilation_size, dilation_size));
// Apply the dilation operation
dilate(src, dilation_dst, element);
imwrite("c:\\001a_dilate.bmp", dilation_dst);
imshow("Dilation Demo", dilation_dst);
waitKey(0);
return 0;
}
1- Find all the contours in your image.
2- Using moments find their center of masses. Example:
/// Get moments
vector<Moments> mu(contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ mu[i] = moments( contours[i], false ); }
/// Get the mass centers:
vector<Point2f> mc( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ mc[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 ); }
3- Create zero(black) image and write all the center points on it.
4- Note that you will have extra one or two points coming from border contours. Maybe you can apply some pre-filtering according to the contour areas, since the border is a big connected contour having large area.
It's not very fast, but I implemented the morphological filtering algorithm from Digital Image Processing, 4th Edition by William K. Pratt. This should be exactly what you're looking for.
The code is MIT licensed and available on GitHub at cgmb/shrink.
Specifically, I've defined cv::Mat cgmb::shrink_max(cv::Mat in) to shrink a given cv::Mat of CV_8UC1 type until no further shrinking can be done.
So, if we compile Shrink.cxx with your program and change your code like so:
#include "Shrink.h" // add this line
...
dilate(src, dilation_dst, element);
dilation_dst = cgmb::shrink_max(dilation_dst); // and this line
imwrite("c:\\001a_dilate.bmp", dilation_dst);
We get this:
By the way, your image revealed a bug in Octave Image's implementation of bwmorph shrink. Figure 2 should not be the result of a shrink operation on Figure 3, as the ring shouldn't be broken by a shrink operation. If that ring disappeared in MATLAB, it presumably also suffers from some sort of similar bug.
At present, Octave and I have slightly different results from MATLAB, but they're pretty close.

opencv image window/imshow

I am just starting to use the Open CV library and one of my first code is a simple negative transform function.
#include <stdio.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
void negative(Mat& input,Mat& output)
{
int row = input.rows;
int col = input.cols;
int x,y;
uchar *input_data=input.data;
uchar *output_data= output.data;
for( x=0;x<row;x++)
for( y=0;y<col;y++)
output_data[x*col+y]=255-input_data[x*col+y];
cout<<x<<y;
}
int main( int argc, char** argv )
{
Mat image;
image = imread( argv[1], 1 );
Mat output=image.clone();
negative(image,output);
namedWindow( "Display Image", CV_WINDOW_AUTOSIZE );
imshow( "Display Image", output );
waitKey(0);
return 0;
}
I have added the extra line to check if the entire image is processed. The problem i am facing with my output image is that negative transform is applied only to top half of the image.
Now what happens is that the values for x and y are displayed only after i press a key (i.e. once the image is shown)
My question is why is the window being called before the function is executed ?
The fundamental problem in your code is that you are reading in a color image but you try to process it as grayscale. Therefore the indices shift and what really happens is that you only process the first third of the image (because of the 3-channel format).
See opencv imread manual
flags –
Specifies color type of the loaded image:
>0 the loaded image is forced to be a 3-channel color image
=0 the loaded image is forced to be grayscale
You've specified flags=1.
Here's a way of doing it:
Vec3b v(255, 255, 255);
for(int i=0;i<input.rows;i++) //search for edges
{
for (int j=0 ;j<input.cols;j++)
{
output.at<Vec3b>(i,j) = v - input.at<Vec3b>(i,j);
}
}
Note that here Vec3b is a 3-channel pixel value as opposed to uchar which is a 1-channel value.
For a more efficient implementation you can have a look at Mat.ptr<Vec3b>(i).
EDIT:
If you are processing lots of images,
for a general iteration over the pixels the fastest way is:
Vec3b v(255, 255, 255); // or maybe Scalar v(255,255,255) Im not sure
for(int i=0;i<input.rows;i++) //search for edges
{
Vec3b *p=input.ptr<Vec3b>(i);
Vec3b *q=output.ptr<Vec3b>(i);
for (int j=0 ;j<input.cols;j++)
{
q[j] = v - p[j];
}
}
See "The OpenCV Tutorials" -- "The efficient way" section.
Try to write:
cout << x << y << endl;
The function is called before, but the output is not flushed directly, which results in your image appearing before the text is written. By adding an "endline", you force a flush. You could also use flush(cout); instead of adding and endline.
For the negative, you can use the OpenCV function subtract() directly:
subtract(Scalar(255, 255, 255), input, output);

Need help implementing a special edge detector

I'm implementing an approach from a research paper. Part of the approach calls for a major edge detector, which the authors describe as follows:
Obtain DC image (effectively downsample by 8 for both width and height)
Calculate Sobel gradient of DC image
Threshold Sobel gradient image (using T=120)
Morphological operations to clean up edge image
Note that this NOT Canny edge detection -- they don't bother with things like non-maximum suppression, etc. I could of course do this with Canny edge detection, but I want to implement things exactly as they are expressed in the paper.
That last step is the one I'm a bit stuck on.
Here is exactly what the authors say about it:
After obtaining the binary
edge map from the edge detection process, a binary morphological
operation is employed to remove isolated edge pixels,
which might cause false alarms during the edge detection
Here's how things are supposed to look like at the end of it all (edge blocks have been filled in black):
Here's what I have if I skip the last step:
It seems to be on the right track. So here's what happens if I do erosion for step 4:
I've tried combinations of erosion and dilation to obtain the same result as they do, but don't get anywhere close. Can anyone suggest a combination of morphological operators that will get me the desired result?
Here's the binarization output, in case anyone wants to play around with it:
And if you're really keen, here is the source code (C++):
#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <assert.h>
using cv::Mat;
using cv::Size;
#include <stdio.h>
#define DCTSIZE 8
#define EDGE_PX 255
/*
* Display a matrix as an image on the screen.
*/
void
show_mat(char *heading, Mat const &m)
{
Mat clone = m.clone();
Mat scaled(clone.size(), CV_8UC1);
convertScaleAbs(clone, scaled);
IplImage ipl = scaled;
cvNamedWindow(heading, CV_WINDOW_AUTOSIZE);
cvShowImage(heading, &ipl);
cvWaitKey(0);
}
/*
* Get the DC components of the specified matrix as an image.
*/
Mat
get_dc(Mat const &m)
{
Size s = m.size();
assert(s.width % DCTSIZE == 0);
assert(s.height % DCTSIZE == 0);
Size dc_size = Size(s.height/DCTSIZE, s.width/DCTSIZE);
Mat dc(dc_size, CV_32FC1);
cv::resize(m, dc, dc_size, 0, 0, cv::INTER_AREA);
return dc;
}
/*
* Detect the edges:
*
* Sobel operator
* Thresholding
* Morphological operations
*/
Mat
detect_edges(Mat const &src, int T)
{
Mat sobelx = Mat(src.size(), CV_32FC1);
Mat sobely = Mat(src.size(), CV_32FC1);
Mat sobel_sum = Mat(src.size(), CV_32FC1);
cv::Sobel(src, sobelx, CV_32F, 1, 0, 3, 0.5);
cv::Sobel(src, sobely, CV_32F, 0, 1, 3, 0.5);
cv::add(cv::abs(sobelx), cv::abs(sobely), sobel_sum);
Mat binarized = src.clone();
cv::threshold(sobel_sum, binarized, T, EDGE_PX, cv::THRESH_BINARY);
cv::imwrite("binarized.png", binarized);
//
// TODO: this is the part I'm having problems with.
//
#if 0
//
// Try a 3x3 cross structuring element.
//
Mat elt(3,3, CV_8UC1);
elt.at<uchar>(0, 1) = 0;
elt.at<uchar>(1, 0) = 0;
elt.at<uchar>(1, 1) = 0;
elt.at<uchar>(1, 2) = 0;
elt.at<uchar>(2, 1) = 0;
#endif
Mat dilated = binarized.clone();
//cv::dilate(binarized, dilated, Mat());
cv::imwrite("dilated.png", dilated);
Mat eroded = dilated.clone();
cv::erode(dilated, eroded, Mat());
cv::imwrite("eroded.png", eroded);
return eroded;
}
/*
* Black out the blocks in the image that contain DC edges.
*/
void
censure_edge_blocks(Mat &orig, Mat const &edges)
{
Size s = edges.size();
for (int i = 0; i < s.height; ++i)
for (int j = 0; j < s.width; ++j)
{
if (edges.at<float>(i, j) != EDGE_PX)
continue;
int row = i*DCTSIZE;
int col = j*DCTSIZE;
for (int m = 0; m < DCTSIZE; ++m)
for (int n = 0; n < DCTSIZE; ++n)
orig.at<uchar>(row + m, col + n) = 0;
}
}
/*
* Load the image and return the first channel.
*/
Mat
load_grayscale(char *filename)
{
Mat orig = cv::imread(filename);
std::vector<Mat> channels(orig.channels());
cv::split(orig, channels);
Mat grey = channels[0];
return grey;
}
int
main(int argc, char **argv)
{
assert(argc == 3);
int bin_thres = atoi(argv[2]);
Mat orig = load_grayscale(argv[1]);
//show_mat("orig", orig);
Mat dc = get_dc(orig);
cv::imwrite("dc.png", dc);
Mat dc_edges = detect_edges(dc, bin_thres);
cv::imwrite("dc_edges.png", dc_edges);
censure_edge_blocks(orig, dc_edges);
show_mat("censured", orig);
cv::imwrite("censured.png", orig);
return 0;
}
I can't imagine any combination of morphological operations that would produce the same edges as detected by the supposedly correct result, given your partial result as input.
I note that the underlying image is different; this probably contributes to why your results are so different. The Lena image is fine for indicating the type of result but not for comparisons. Do you have the exact same image as the original authors ?
What the authors described could be implemented with connected component analysis, using 8way connectivity. I would not call that morphological though.
I do think you are missing something else: Their image does not have edges that are thicker than one pixel. Yours has. The paragraph you quoted only talks about removing isolated pixels, so there must be a step you missed or implemented differently.
Good luck!
I think that what you need is a kind of erode or open that is, in a sense, 4-way and not 8-way. The default morphological kernel for OpenCV is a 3x3 rectangle (IplConvKernel with shape=CV_SHAPE_RECT). This is pretty harsh on thin edges.
You might want to try eroding with a 3x3 custom IplConvKernel with shape=CV_SHAPE_CROSS.
If you need an even finer filter, you may want to try eroding with 4 different CV_SHAPE_RECT kernels of size 1x2, 2x1 with the anchor in (0,1) and (1,0) for each.
First of all, your input image has a much higher resolution that the test input image, which can explain the fact less edges are detected - the changes are more smooth.
Second of all, since the edges are thresholded to 0, try dilation on smaller neighborhoods (e.g. compare each pixels with 4 original neighbors (in a non-serial manner)) to get rid of isolated edges.