I am trying to implement the Matlab function imquantize using opencv. Which opencv thresholding function I should use to implement Matlab function multithresh? Once thresholding has been done how do I label the pixels according to the threshold? Is this the right way to implement imquantize ? Are there any other function's I should include in the code?
There is an implementation based on OpenCV here, where you should probably get the idea:
cv::Mat
imquantize(const cv::Mat& in, const arma::fvec& thresholds) {
BOOST_ASSERT_MSG(cv::DataType<float>::type == in.type(), "input is not of type float");
cv::Mat index(in.size(), in.type(), cv::Scalar::all(1));
for (int i = 0; i < thresholds.size() ; i++) {
cv::Mat temp = (in > thresholds(i)) / 255;
temp.convertTo(temp, cv::DataType<float>::type);
index += temp;
}
return index;
}
Updated: thresholds are the vector of the float threshold values (uniform distributed to # of levels that you want to quantize within [0, 1]). Check the code snippet of how it is used:
const float step = 1./levels[i];
arma::fvec thresh = arma::linspace<arma::fvec>(step, 1.-step, levels[i]-1);
channels[i] = imquantize(channels[i], thresh);
I suppose you are looking for something like this
/*function imquantize
* 'inputImage' is the input image.
* 'levels' is an array of threholds
* 'quantizedImage' is the reurned image
* with quantized levels.
*/
Mat imquantize(Mat inputImage, vector<vector<int> > levels)
{
//initialise output label matrix
Mat quantizedImage(inputImage.size(), inputImage.type(), Scalar::all(1));
//Apply labels to the pixels according to the thresholds
for (int i = 0; i < inputImage.cols; i++)
{
for (int j = 0; j < inputImage.rows; j++)
{
// Check if image is grayscale or BGR
if(levels.size() == 1)
{
for (int k = 0; k < levels[0].size(); k++)
{
// if pixel < lowest threshold , then assign 0
if(inputImage.at<uchar>(j,i) <= levels[0][0])
{
quantizedImage.at<uchar>(j,i) = 0;
}
// if pixel > highest threshold , then assign 255
else if(inputImage.at<uchar>(j,i) >= levels[0][levels[0].size()-1])
{
quantizedImage.at<uchar>(j,i) = 255;
}
// Check the level borders for pixel and assign the corresponding
// upper bound quanta to the pixel
else
{
if(levels[0][k] < inputImage.at<uchar>(j,i) && inputImage.at<uchar>(j,i) <= levels[0][k+1])
{
quantizedImage.at<uchar>(j,i) = (k+1)*255/(levels[0].size());
}
}
}
}
else
{
Vec3b pair = inputImage.at<Vec3b>(j,i);
// Processing the Blue Channel
for (int k = 0; k < levels[0].size(); k++)
{
if( pair.val[0] <= levels[0][0])
{
quantizedImage.at<Vec3b>(j,i)[0] = 0;
}
else if( pair.val[0] >= levels[0][levels.size()-1])
{
quantizedImage.at<Vec3b>(j,i)[0] = 255;
}
else
{
if(levels[0][k] < pair.val[0] && pair.val[0] <= levels[0][k+1])
{
quantizedImage.at<Vec3b>(j,i)[0] = (k+1)*255/(levels[0].size());
}
}
}
// Processing the Green Channel
for (int k = 0; k < levels[1].size(); k++)
{
if( pair.val[1] <= levels[1][0])
{
quantizedImage.at<Vec3b>(j,i)[1] = 0;
}
else if( pair.val[1] >= levels[1][levels.size()-1])
{
quantizedImage.at<Vec3b>(j,i)[1] = 255;
}
else
{
if(levels[1][k] < pair.val[1] && pair.val[1] <= levels[1][k+1])
{
quantizedImage.at<Vec3b>(j,i)[1] = (k+1)*255/(levels[1].size());
}
}
}
// Processing the Red Channel
for (int k = 0; k < levels[2].size(); k++)
{
if( pair.val[2] <= levels[2][0])
{
quantizedImage.at<Vec3b>(j,i)[2] = 0;
}
else if( pair.val[2] >= levels[2][levels.size()-1])
{
quantizedImage.at<Vec3b>(j,i)[2] = 255;
}
else
{
if(levels[2][k] < pair.val[2] && pair.val[2] <= levels[2][k+1])
{
quantizedImage.at<Vec3b>(j,i)[2] = (k+1)*255/(levels[2].size());
}
}
}
}
}
}
return quantizedImage;
}
In this function the input had to be an Mat::Image and a 2D vector which can have different levels for different channels.
Related
This is the part of code that I've written to build the matrix to calculate mean threshold of specific zone in multiple images.
The problem is that I get the value of mean threshold: -2147483648
(Apologies for language error)
Here is my code:
int moyenne(cv::Mat& image, cv::Point seed) {
double th = 0;
int count = 0;
int N = 3;
for (int x = seed.x - N; x <= seed.x + N; x++) {
for (int y = seed.y - N; y <= seed.y + N; y++) {
if (x == seed.x && y == seed.y) {
continue;
}
else {
int som = 0;
som = +(abs(int(image.at<cv::Vec3b>(seed.x, seed.y)[0])));
if (som == N) {
N++;
}
while (som <N ) {
th += abs(int(image.at<cv::Vec3b>(seed)[0] - image.at<cv::Vec3b>(x, y)[0]));
count++;
}
}
}
} return th / count;
}
I am trying to implement a function that erodes a picture without using the built in erosion function in opencv.
My approach is to check if there are any 0's in the image area and if the kernel is one then I can't set the one, which means the condition is false.
Then my second if statement means that the condition is true then my 1 is set .
void erosion(const cv::Mat& image, cv::Mat& erosion_image, const cv::Mat& kernel)
{
bool check;
int count;
int count_2;
int anchorx = kernel.rows / (2.0);
int anchory = kernel.cols / (2.0);
for (int x = 0; x < image.rows; x++) {
for (int y = 0; y < image.cols; y++) {
kernel.at<uchar>(x, y);
for (int count = 0; count < kernel.rows; count++) {
for (int count_2 = 0; count_2 < kernel.cols; count_2++) {
if (image.at<uchar>(x + count, y + count_2) == 0 && kernel.at<uchar>(count, count_2) == 1) {
check = false;
}
}
}
if (check) {
erosion_image.at<uchar>(x, y) = 1;
}
}
}
}
is this the right approach?
thank you in advance
This is define of erosion for binary images. If you process grayscale image you should set a pixel value to minimum value of the points in its neighborhood Wiki-Erosion.
In following code instead of maximum pixel value 1 i used 255 - default for OpenCV maximum value of CV_8U image type.
Note, you also need to choose how to process border pixels of image. In following code they just not processed.
The erosion for binary image can be represented as something like that:
void erosion(const cv::Mat& image, cv::Mat& erosion_image, const cv::Mat& kernel)
{
bool check;
int count;
int count_2;
int anchorx = kernel.rows / (2.0);
int anchory = kernel.cols / (2.0);
for (int x = anchorx; x < image.rows - anchorx; x++) {
for (int y = anchory; y < image.cols - anchory; y++) {
check = true;
if (image.at<uchar>(x, y))
{
for (int count = 0; count < kernel.rows && check; count++) {
for (int count_2 = 0; count_2 < kernel.cols && check; count_2++) {
if (image.at<uchar>(x - anchorx + count, y - anchory + count_2) == 0 &&
kernel.at<uchar>(count, count_2) == 1) {
erosion_image.at<uchar>(x, y) = 0;
check = false;
}
}
}
if (check)
erosion_image.at<uchar>(x, y) = 255;
}
else
erosion_image.at<uchar>(x, y) = 0;
}
}
}
I am trying to implement laplacian filter for sharpening an image.
but the result is kinda grey , I don't know what went wrong with my code.
Here's my work so far
img = imread("moon.png", 0);
Mat convoSharp() {
//creating new image
Mat res = img.clone();
for (int y = 0; y < res.rows; y++) {
for (int x = 0; x < res.cols; x++) {
res.at<uchar>(y, x) = 0.0;
}
}
//variable declaration
//change -5 to -4 for original result.
int filter[3][3] = { {0,1,0},{1,-4,1},{0,1,0} };
//int filter[3][3] = { {-1,-2,-1},{0,0,0},{1,2,1} };
int height = img.rows;
int width = img.cols;
int **temp = new int*[height];
for (int i = 0; i < height; i++) {
temp[i] = new int[width];
}
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
temp[i][j] = 0;
}
}
int filterHeight = 3;
int filterWidth = 3;
int newImageHeight = height - filterHeight + 1;
int newImageWidth = width - filterWidth + 1;
int i, j, h, w;
//convolution
for (i = 0; i < newImageHeight; i++) {
for (j = 0; j < newImageWidth; j++) {
for (h = i; h < i + filterHeight; h++) {
for (w = j; w < j + filterWidth; w++) {
temp[i][j] += filter[h - i][w - j] * (int)img.at<uchar>(h, w);
}
}
}
}
//find max and min
int max = 0;
int min = 100;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (temp[i][j] > max) {
max = temp[i][j];
}
if (temp[i][j] < min) {
min = temp[i][j];
}
}
}
//clamp 0 - 255
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
res.at<uchar>(i, j) = 0 + (temp[i][j] - min)*(255 - 0) / (max - min);
}
}
//empty the temp array
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
temp[i][j] = 0;
}
}
//img - res and store it in temp array
for (int y = 0; y < res.rows; y++) {
for (int x = 0; x < res.cols; x++) {
//int a = (int)img.at<uchar>(y, x) - (int)res.at<uchar>(y, x);
//cout << a << endl;
temp[y][x] = (int)img.at<uchar>(y, x) - (int)res.at<uchar>(y, x);
}
}
//find the new max and min
max = 0;
min = 100;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
if (temp[i][j] > max) {
max = temp[i][j];
}
if (temp[i][j] < min) {
min = temp[i][j];
}
}
}
//clamp it back to 0-255
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
res.at<uchar>(i, j) = 0 + (temp[i][j] - min)*(255 - 0) / (max - min);
temp[i][j] = (int)res.at<uchar>(i, j);
}
}
return res;
}
And here's the result
as you can see in my code above , i already normalize the pixel value to 0-255. i still don't know what went wrong here. Can anyone here explain why is that ?
The greyness is because, as Max suggested in his answer, you are scaling to the 0-255 range, not clamping (as your comments in the code suggest).
However, that is not all of the issues in your code. The output of the Laplace operator contains negative values. You nicely store these in an int. But then you scale and copy over to a char. Don't do that!
You need to add the result of the Laplace unchanged to your image. This way, some pixels in your image will become darker, and some lighter. This is what causes the edges to appear sharper.
Simply skip some of the loops in your code, and keep one that does temp = img - temp. That result you can freely scale or clamp to the output range and cast to char.
To clamp, simply set any pixel values below 0 to 0, and any above 255 to 255. Don't compute min/max and scale as you do, because there you reduce contrast and create the greyish wash over your image.
Your recent question is quite similar (though the problem in the code was different), read my answer there again, it suggests a way to further simplify your code so that img-Laplace becomes a single convolution.
The problem is that you are clamping and rescaling the image. Look at the bottom left border of the moon: There are very bright pixels next to very dark pixels, and then some gray pixels right besides the bright ones. Your sharpening filter will really spike on that bright border and increase the maximum. Similarly, the black pixels will be reduced even further.
You then determine minimum and maximum and rescale the entire image. This necessarily means the entire image will lose contrast when displayed in the previous gray scale, because your filter outputted pixel values above 255 and below 0.
Looks closely at the border of the moon in the output image:
There is a black halo (the new 0) and a bright, sharp edge (the new 255). (The browser image scaling made it less crisp in this screenshot, look at your original output). Everything else was squashed by the rescaling, so what was previous black (0) is now dark gray.
I found this code and I wonder why it does't works ..
// f is an stream
void writeTGA(ostream& f, bool rle) const{
f<<ubyte(0);//ID length
f<<ubyte(0);//Color map Type
if(rle)
f<<ubyte(10);//Image Type Code
else
f<<ubyte(2);//Image Type Code
f<<ubyte(0);//Color map stuff
f<<ubyte(0);
f<<ubyte(0);
f<<ubyte(0);
f<<ubyte(0);
f<<ubyte(0);//X Origin of Image
f<<ubyte(0);//X Origin of Image
f<<ubyte(0);//Y Origin of Image
f<<ubyte(0);//Y Origin of Image
f << ubyte(width%256);
f << ubyte(width>>8);
f << ubyte(height%256);
f << ubyte(height>>8);
f<<ubyte(24);//Image Pixels Size
f<<ubyte(32);//Image Descriptor Byte
if(rle)
{
cout<<"rleHeadHead"<<endl;
ubyte rleHead = 0;
ubyte diff[128];
int i = 0;
while(i<width*height)
{
rleHead = 1;
/* RLE */
if(i+1 < width*height)
while(pixels[i] == pixels[i+1]) {
if(i+1 >= width*height || rleHead >= 128)
break;
rleHead++;
i++;
}
if(rleHead > 1)
{
f<< (rleHead+127);
f<<pixels[i].b;
f<<pixels[i].g;
f<<pixels[i].r;
}
rleHead = 0;
/* RAW */
if(i+1 < width*height)
{
while(pixels[i+rleHead] != pixels[i+rleHead+1])
{
if( (i+rleHead+1) >= width*height || rleHead >= 128)
break;
rleHead++;
}
} else
rleHead++;
if(rleHead > 0)
{
f << (rleHead-1);
for(int j = 0; j < rleHead; j++)
{
diff[j] = pixels[i+j].b;
diff[j] = pixels[i+j].g;
diff[j] = pixels[i+j].r;
}
f.write((const char*) diff, rle*3);
i += rleHead;
}}}
else{
for(int i = 0 ; i < width*height ; i++){
f<< pixels[i].b;
f<< pixels[i].g;
f<< pixels[i].r;}
}
}
I tried to implement it and it seems not good ..
Otherwise, someone know if it exist a library or just a simple file where I can find this algorithm ?
Thanks you in advance
I'm trying to get the number of difference between two pictures.
When I compare 2 images in gray scale, pixDiff <> 0 but when it come to RGB, pixDiff is always 0.
I used openCV's compare and also a custom loop.
Mat frame, oldFrame;
cap >> oldFrame;
if(analyseMod == MONOCHROME)
cvtColor(oldFrame, oldFrame, CV_BGR2GRAY);
nbChannels = oldFrame.channels();
while(1)
{
pixDiff = 0;
cap >> frame;
//Test diff
Mat diff;
compare(oldFrame, frame, diff, CMP_NE);
imshow("video 0", diff);
imshow("video 1", frame);
if(analyseMod == MONOCHROME)
{
cvtColor(frame, frame, CV_BGR2GRAY);
for(int i=0; i<frame.rows; i++)
for(int j=0; j<frame.cols; j++)
if(frame.at<uchar>(i,j) < oldFrame.at<uchar>(i,j) - similarPixelTolerance || frame.at<uchar>(i,j) > oldFrame.at<uchar>(i,j) + similarPixelTolerance)
pixDiff++;
}
else if(analyseMod == RGB)
{
uint8_t *f = (uint8_t *)frame.data;
uint8_t *o = (uint8_t *)oldFrame.data;
for(int i=0; i<frame.rows; i++)
{
for(int j=0; j<frame.cols; j++)
{
if(f[nbChannels*i*frame.cols + j + RED] < o[nbChannels*i*oldFrame.cols + j + RED])
pixDiff++;
}
}
}
frame.copyTo(oldFrame);
cout << pixDiff;
if(waitKey(30) >= 0) break;
}
Thx for help
I still don't get it, why are you not using your delta in the RGB case, but here is the solution for both cases, if you want to consider color channels separately. Set CN to 1 for monochrome case and to 3 for RGB case.
const int CN = 3; // 3 for RGB, 1 for monochrome
uint8_t *f = frame.ptr<uint8_t>();
uint8_t *o = oldFrame.ptr<uint8_t>();
for(int i = 0; i < frame.rows; ++i)
{
for(int j = 0; j < frame.cols; ++j)
{
for (int c = 0; c < CN; ++c)
{
if (abs(*f - *o) > similarPixelTolerance) ++pxDiff;
++f, ++o;
}
}
}
It is way more efficient to access pixels in this way than to call at for each pixel. The only possible problem is if you have some padding in your images, but by default OpenCV is using continuous allocation.