How to improve intensity contrast in image? - image

I have an image with very low intensity contrast from its background.
The first line between the two arrows is the line with low contrast.
The second line is ok. Please see in the below image.
The original image is as shown below.
I used the following method to enhance the contrast in Gray scale.
First the image is changed to Gray color and used the following method.
cv::Mat temp;
for (int i = 0; i < 1; i++) // number of iterations has to be adjusted
{
cv::threshold(image, temp, 0, 255, CV_THRESH_BINARY| CV_THRESH_OTSU);//
cv::bitwise_and(image, temp, image);
cv::normalize(image, image, 0, 255, cv::NORM_MINMAX, -1, temp);
}
I have image with a little bit higher in contrast in Gray scale, but is there any method better than this in Gray scale or Color?

I would look at histogram equalization, that might serve your needs. Basic (global) equalization or even adaptive can yield great results. Parameters will likely need to be tuned for the adaptive method (using the one from the docs example for now).
I get (global equalization - left; adaptive equalization - right):
Once the equalization is done, you might have better luck with thresholding (though your example is very low contrast):
From there, you can use standard contour/shape matching etc to try to find the location of your 1st black line.
Gotten from
import cv2
import matplotlib.pyplot as plt
import numpy as np
raw_img_load = cv2.imread('H1o8X.png')
imgr = cv2.cvtColor(raw_img_load,cv2.COLOR_BGR2GRAY)
clahe = cv2.createCLAHE(clipLimit=30.0, tileGridSize=(8,8))
imgray_ad = clahe.apply(imgr)#adaptive
imgray = cv2.equalizeHist(imgr)#global
res = np.hstack((imgray,imgray_ad))#so we can plot together
plt.imshow(res,cmap='gray')
plt.show()
ret,thresh = cv2.threshold(imgray_ad,150,255,type=cv2.THRESH_BINARY+cv2.THRESH_OTSU)
plt.imshow(thresh,cmap='gray')
plt.show()
EDIT: based on #Doleron's answer, for this particular problem I would recommend using fastNlMeansDenoising (applied before any histogram equalization). Note, however, that it can be a slow function for high-res images/time-sensitive image processing.

The #Antoine Zambelli answer is awsome and it is the correct one. Anyway, I dug some here and and tried to remove the noise previously with fastNlMeansDenoising to improve the final result:
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/photo.hpp"
using namespace cv;
using cv::CLAHE;
int main(int argc, char** argv) {
Mat srcImage = imread("H1o8X.png", CV_LOAD_IMAGE_GRAYSCALE);
imshow("src", srcImage);
Mat denoised;
fastNlMeansDenoising(srcImage, denoised, 10);
Mat image = denoised;
Ptr<CLAHE> clahe = createCLAHE();
clahe->setClipLimit(30.0);
clahe->setTilesGridSize(Size(8, 8));
Mat imgray_ad;
clahe->apply(image, imgray_ad);
Mat imgray;
cv::equalizeHist(image, imgray);
imshow("imgray_ad", imgray_ad);
imshow("imgray", imgray);
Mat thresh;
threshold(imgray_ad, thresh, 150, 255, THRESH_BINARY | THRESH_OTSU);
imshow("thresh", thresh);
Mat result;
Mat kernel = Mat::ones(8, 8, CV_8UC1);
erode(thresh, result, kernel);
imshow("result", result);
waitKey();
return 0;
}

Related

Creating a simple black image with opencv using cvcreateimage

Very basic question coming from a newbie in OpenCV. I just want to create an image with every pixel set to 0 (black). I have used the following code in the main() function:
IplImage* imgScribble = cvCreateImage(cvSize(320, 240), 8, 3);
And what I get is a solid gray image, instead of the black one.
Thanks in advance !
What version of opencv you are using?
For Mat,
#include <opencv2/opencv.hpp>
cv::Mat image(320, 240, CV_8UC3, cv::Scalar(0, 0, 0));
I can suggest two more altrnatives:
IplImage* imgScribble = cvCreateImage(cvSize(320, 240), 8, 3);
// Here you can set any color
cvSet(imgScribble, cvScalar(0,0,0));
// Here only black
cvZero(imgScribble);
The call to
cvCreateImage(cvSize(320, 240), 8, 3);
Create the image in the memory, but I don't think it initialize the data.
You should try this to initialize :
step= imgScribble->widthStep;
data = (uchar *)imgScribble->imageData;
for(i=0;i<imgScribble->height;i++) for(j=0;j<img->width;j++) for(k=0;k<3;k++)
data[i*step+j*3+k]=0;
(Inspired from this (Example C Program))
For Python:
import numpy as np
X_DIMENSION = 288
Y_DIMENSION = 382
black_image = np.zeros((X_DIMENSION, Y_DIMENSION))
With this code you generate a numpy array which is what is expected for opencv images and fill it with zero which is the color for black. This code is made for grayscale images. If you want it to be an RGB black image, just add 3 at the end of the tupple to create the dimensions np.zeros((X_DIMENSION, Y_DIMENSION, 3))
black and white image mean single channel image. you can simply created it as follows.
Mat img(500, 1000, CV_8UC1, Scalar(a));
"a" in between 0-255
you can see more examlpe and details from following page
https://progtpoint.blogspot.com/2017/01/tutorial-3-create-image.html
Here is my contribution:
cv::Mat output = cv::Mat::zeros(cv::Size(320, 240), CV_8UC3);
#include "stdafx.h"
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
#include <iostream>
using namespace cv;
using namespace std;
#define LOAD_OPTION CV_LOAD_IMAGE_COLOR
int main( int argc, char** argv )
{
IplImage *image;
image = cvLoadImage("picture.jpg",0); // 0 : BLACK AND WHITE , Without 0 -> Color Picture
cvNamedWindow("Image",CV_WINDOW_AUTOSIZE);
cvShowImage("Image", image);
waitKey(-1);
return 0;
}

finding the skin tone percentage of a person from an image (not a web cam feed ) ignoring the background

I need to find the percentage of skintone of a person in a given image.
I have been able to count all the pixels with skin colour so far but I am having trouble ignoring the background of the person so I can count the number of pixels for the percentage.
BackgroundSubtractorMOG2 bg;
bg.nmixtures =3;
bg.bShadowDetection=false;
bg.operator ()(img,fore);
bg.getBackgroundImage(back);
img is my image. I was trying to separate the back and fore mat objects, but with the above code snippet back and fore take the same value as the img. Nothing is happening.
Can you point me in the right direction as to what changes I have to make to get it right?
I was able to run some similar code found here:
http://mateuszstankiewicz.eu/?p=189
I had to change a couple of things, but it ended up working properly (back and fore are not the same as img when displayed:
int main(int argc, char *argv[]) {
Mat frame, back, fore;
VideoCapture cap(0);
BackgroundSubtractorMOG2 bg;
vector<std::vector<Point> > contours;
namedWindow("Frame");
namedWindow("Background");
namedWindow("Foreground");
for(;;) {
cap >> frame;
bg.operator ()(frame, fore);
bg.getBackgroundImage(back);
erode(fore, fore, Mat());
dilate(fore, fore, Mat());
findContours(fore, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(frame, contours, -1, Scalar(0, 0, 255), 2);
imshow("Frame", frame);
imshow("Background", back);
imshow("Foreground", fore);
if(waitKey(1) == 27) break;
}
return 0;
}

Which is the most efficient way to do alpha mask in opencv?

I know OpenCV only supports binary masks.
But I need to do an overlay where I have a grayscale mask that specifies transparency of the overlay.
Eg. if a pixel in the mask is 50% white it should mean a cv::addWeighted operation for that pixel with alpha=beta=0.5, gamma = 0.0.
Now, if there is no opencv library function, what algorithm would you suggest as the most efficient?
I did something like this for a fix.
typedef double Mask_value_t;
typedef Mat_<Mask_value_t> Mask;
void cv::addMasked(const Mat& src1, const Mat& src2, const Mask& mask, Mat& dst)
{
MatConstIterator_<Vec3b> it1 = src1.begin<Vec3b>(), it1_end = src1.end<Vec3b>();
MatConstIterator_<Vec3b> it2 = src2.begin<Vec3b>();
MatConstIterator_<Mask_value_t> mask_it = mask.begin();
MatIterator_<Vec3b> dst_it = dst.begin<Vec3b>();
for(; it1 != it1_end; ++it1, ++it2, ++mask_it, ++dst_it)
*dst_it = (*it1) * (1.0-*mask_it) + (*it2) * (*mask_it);
}
I have not optimized nor made safe this code yet with assertions.
Working assumptions: all Mat's and the Mask are the same size and Mat's are normal three channel color images.
I have a similar problem, where I wanted to apply a png with transparency.
My solution was using Mat expressions:
void AlphaBlend(const Mat& imgFore, Mat& imgDst, const Mat& alpha)
{
vector<Mat> vAlpha;
Mat imgAlpha3;
for(int i = 0; i < 3; i++) vAlpha.push_back(alpha);
merge(vAlpha,imgAlpha3)
Mat blend = imgFore.mul(imgAlpha3,1.0/255) +
imgDst.mul(Scalar::all(255)-imgAlpha3,1.0/255);
blend.copyTo(imgDst);
}
OpenCV supports RGBA images which you can create by using mixchannels or the split and merge functions to combine your images with your greyscale mask. I hope this is what you are looking for!
Using this method you can combine your grayscale mask with your image like so:
cv::Mat gray_image, mask, rgba_image;
std::vector<cv::Mat> result;
cv::Mat image = cv::imread(image_path);
cv::split(image, result);
cv::cvtColor(image, gray_image, CV_BGR2GRAY);
cv::threshold(gray_image, mask, 128, 255, CV_THRESH_BINARY);
result.push_back(mask);
cv::merge(result, rgba_image);
imwrite("rgba.png", rgba_image);
Keep in mind that you cannot view RGBA images using cv::imshow as described in read-rgba-image-opencv and you cannot save your image as jpeg since that format does not support transparency. It seems that you can combine channels using cv::cvtcolor as shown in opencv-2-3-convert-mat-to-rgba-pixel-array

How to apply Gaussian filter to DFT output in OpenCV

I want to create a Gaussian high-pass filter after determining the correct padding size (e.g., if image width and height is 10X10, then should be 20X20).
I have Matlab code that I am trying to port in OpenCV, but I am having difficulty properly porting it. My Matlab code is show below:
f1_seg = imread('thumb1-small-test.jpg');
Iori = f1_seg;
% Iori = imresize(Iori, 0.2);
%Convert to grayscale
I = Iori;
if length(size(I)) == 3
I = rgb2gray(Iori);
end
%
%Determine good padding for Fourier transform
PQ = paddedsize(size(I));
I = double(I);
%Create a Gaussian Highpass filter 5% the width of the Fourier transform
D0 = 0.05*PQ(1);
H = hpfilter('gaussian', PQ(1), PQ(2), D0);
% Calculate the discrete Fourier transform of the image.
F=fft2(double(I),size(H,1),size(H,2));
% Apply the highpass filter to the Fourier spectrum of the image
HPFS_I = H.*F;
I know how to use the DFT in OpenCV, and I am able to generate its image, but I am not sure how to create the Gaussian filter. Please guide me to how I can create a high-pass Gaussian filter as is shown above?
I believe where you are stuck is that the Gaussian filter supplied by OpenCV is created in the spatial (time) domain, but you want the filter in the frequency domain. Here is a nice article on the difference between high and low-pass filtering in the frequency domain.
Once you have a good understanding of how frequency domain filtering works, then you are ready to try to create a Gaussian Filter in the frequency domain. Here is a good lecture on creating a few different (including Gaussian) filters in the frequency domain.
If you are still having difficulty, I will try to update my post with an example a bit later!
EDIT :
Here is a short example that I wrote on implementing a Gaussian high-pass filter (based on the lecture I pointed you to):
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
double pixelDistance(double u, double v)
{
return cv::sqrt(u*u + v*v);
}
double gaussianCoeff(double u, double v, double d0)
{
double d = pixelDistance(u, v);
return 1.0 - cv::exp((-d*d) / (2*d0*d0));
}
cv::Mat createGaussianHighPassFilter(cv::Size size, double cutoffInPixels)
{
Mat ghpf(size, CV_64F);
cv::Point center(size.width / 2, size.height / 2);
for(int u = 0; u < ghpf.rows; u++)
{
for(int v = 0; v < ghpf.cols; v++)
{
ghpf.at<double>(u, v) = gaussianCoeff(u - center.y, v - center.x, cutoffInPixels);
}
}
return ghpf;
}
int main(int /*argc*/, char** /*argv*/)
{
Mat ghpf = createGaussianHighPassFilter(Size(128, 128), 16.0);
imshow("ghpf", ghpf);
waitKey();
return 0;
}
This is definitely not an optimized filter generator by any means, but I tried to keep it simple and straight forward to ease understanding :) Anyway, this code displays the following filter:
NOTE : This filter is not FFT shifted (i.e., this filter works when the DC is placed in the center instead of the upper-left corner). See the OpenCV dft.cpp sample (lines 62 - 74 in particular) on how to perform FFT shifting in OpenCV.
Enjoy!

what 2 & 3 mean in this and how can i change them CvMat* rot = cvCreateMat(2,3,CV_32FC1)

What do 2 & 3 mean in this and how can I change them?
CvMat* rot = cvCreateMat(2,3,CV_32FC1)
When I change these two values I get an openCV GUI error handler.
size of input arguments do not match()
in function cvConvertScale.\cxconvert.cpp(1601)
I want to understand what that means
Update:
The code is:
#include <cv.h>
#include <highgui.h>
int main()
{
CvMat* rot = cvCreateMat(2,3,CV_32FC1);
IplImage *src, *dst;
src=cvLoadImage("doda.jpg");
// make acopy of gray image(src)
dst = cvCloneImage( src );
dst->origin = src->origin;
// make dstof zeros
cvZero( dst );
// Compute rotation matrix
double x=0.0;
// loop to get rotation from 0 to 360 by 4 press on anykey
for(int i=1;i<=5;i++)
{
CvPoint2D32f center = cvPoint2D32f(src->width/2,src->height/2);
double angle = 0+x;
double scale = 0.6;
cv2DRotationMatrix( center, angle, scale, rot );
// Do the transformation
cvWarpAffine( src, dst, rot);
cvNamedWindow( "Affine_Transform", 1 );
cvShowImage( "Affine_Transform", dst );
if (i<=4)
x=x+90.0;
else
x=0.0;
cvWaitKey();
}
cvReleaseImage( &dst );
cvReleaseMat( &rot );
return 0;
}
2 and 3 are the row and column counts of the matrix you're creating.
From Introduction to programming with OpenCV:
Allocate a matrix:
CvMat* cvCreateMat(int rows, int cols, int type);
type: Type of the matrix elements. Specified in form
CV_<bit_depth>(S|U|F)C<number_of_channels>. E.g.: CV_8UC1 means an
8-bit unsigned single-channel matrix, CV_32SC2 means a 32-bit signed
matrix with two channels.
Example:
CvMat* M = cvCreateMat(4,4,CV_32FC1);
Changing them is as simple as substituting different values. But I guess you should already know that.
2 = number of rows and 3 = number of columns in your matrix, rot.
Can you post the entire code? Or maybe tell us what you want to achieve? Are you trying to rotate an image?
Also, I'd recommend upgrading to OpenCV 2.0 which has a C++ interface. With the new version, you can extensively use the Mat class which handles everything (matrices,images,etc.) and makes things much simpler.
You get an error using any other shape than 2x3 because it is then meaningless for opencv when you use rot for rotation.
Take a look at Jacob's answer.
He describes the rotation matrix components in details.

Resources