OpenCV cvtColor modifies original image too - c++

I have a function that takes an image, converts it to HSV, and sets V to 100. However, it appears to modify the original image too.
Mat hsvfilter(const Mat& img) {
Mat result;
cvtColor(img, result, CV_BGR2HSV);
for (int j = 0; j < img.rows; j++)
for (int i = 0; i < img.cols; i++)
result.at<Vec3d>(i, j)[2] = 100;
return result;
}
Here's how I call it:
Mat original = imread( "pic.png" );
Mat converted = hsvfilter(original);
namedWindow( "original", CV_WINDOW_AUTOSIZE );
imshow( "original", original );
namedWindow( "converted", CV_WINDOW_AUTOSIZE );
imshow( "converted", converted );
waitKey(0);
Both the original image and the converted images end up having strange black vertical bars. I believe my code has some issues with pointers or memory, but I can't quite figure out where. Any help would be appreciated.
EDIT: HERE'S THE FIXED CODE
Mat hsvfilter(const Mat& img) {
Mat result;
cvtColor(img, result, CV_BGR2HSV);
for (int j = 0; j < result.rows; j++) {
for (int i = 0; i < result.cols; i++) {
result.at<cv::Vec3b>(j, i)[2] = 100;
}
}
return result;
}

Your hsvFilter function should looks like this:
Mat hsvfilter(const Mat& img) {
Mat result;
cvtColor(img, result, CV_BGR2HSV);
for (int j = 0; j < result.rows; j++) //you are modyfying "result" object, not img
for (int i = 0; i < result.cols; i++) //same as above
result.at<Vec3d>(j, i)[2] = 100; //OpenCV uses (y,x) indexing
return result;
}
In this situation ther is no difference in using img.cols, img.rows / result.cols, result.rows, because size of both arrays (images) is the same, but generally don't forget about it :) The second comment doesn't need any more explanation.
Generally you code looks fine, in my opinion it should work. Did you try testing it without calling hsvFilter function (just display the original image)?
If you want to keep created windows for some time, use this code instead of waitKey(0);:
while(waitKey(100) != 'q')
{
//all imshow calls
}
Now, when you want to exit, just press 'q' (you need to have one of your app windows active).

Related

Opencv only process the parts of image

I want to make a negative transformation for the image which is a very simple program.
But when I run the program. I want to transform all of the pixels in the image, but only 1/3 parts of that are processed. I don't make sure where is wrong. all the code I followed the book. But the result is different.
I think there is something wrong about the columns, but when I change the value of I.cols in negativeImage function with the actual value of image. the output still keep the same. only 1/3 parts of image are processed. If I 3 times the I.cols all of the pixels in the iamge could be processed.
vector<uchar> getNegativeLUT() {
vector<uchar> LUT(256, 0);
for (int i = 0; i < 256; ++i)
LUT[i] = (uchar)(255 - i);
return LUT;
}
void negativeImage(Mat& I) {
vector<uchar> LUT = getNegativeLUT();
for (int i = 0; i < I.rows; ++i) {
for (int j = 0; j < I.cols; ++j) {
I.at<uchar>(i, j) = LUT[I.at<uchar>(i, j)];
//stack overflow
}
}
}
int main() {
Mat image = imread("1.png");
Mat processed_image2 = image.clone();
negativeImage(processed_image2);
printf("%d", image.cols);
imshow("Input Image", image);
imshow("Negative Image", processed_image2);
waitKey(0);
return 0;
}
Output Image
You need to put correct type with at<> operator. Your PNG image has to be converted to 8UC1 to then use uchar type to access each pixel. I suppose your image has 3 channels, so you only iterate over 1/3 of the image. Also, I suggest you to use ptr<> operator in rows loop and then access to pixel as an array.
Mat M;
cvtColor(I, M, CV_BGR2GRAY);
// M is CV_8UC1 type
for(int i = 0; i < M.rows; i++)
{
uchar* p = M.ptr<uchar>(i);
for(int j = 0; j < I.cols; j++)
{
p[j] = LUT[p[j]];
}
}
EDIT: you should use cv::LUT instead of doing it yourself.
cv::Mat lut(1, 256, CV_8UC1);
for( int i = 0; i < 256; ++i)
{
lut.at<uchar>(0,i) = uchar(255-i);
}
cv::LUT(M, lut, result);

Error in OpenCV: getting \377 while printing intensity values from a MAT

I am trying to access the values in a opencv Mat; but when I print the values I get the following:
\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377
My code is as follows:
cv::Mat oneComp(yb-yt, xb-xt, CV_8U);
cv::Mat currentLabelImage(drawing.rows,drawing.cols, CV_8U);
currentLabelImage = (drawing == n+1);
currentLabelImage.convertTo(currentLabelImage, CV_32F);
for (int i = 0; i<yb-yt; i++){
for(int j = 0; j<xb-xt; j++){
std:: cout << currentLabelImage.at<uchar>(i+yt, j+xt);
}
}
I am doing this on Objective-C Xcode.

How should I create an array of matrices with openCV

I want to create an array of matrices for segment the image.
Here is what I do for creating array, and it shows an error of "EXC_I386_GPFLT".
How can I fix it or What should I do to achieve my purpose?
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "iostream"
using namespace cv;
using namespace std;
int main( )
{
Mat img;
img = imread("/Users/koike1979/Documents/0306/trucka.bmp", CV_LOAD_IMAGE_COLOR);
namedWindow( "Original image", CV_WINDOW_AUTOSIZE );
imshow( "Original image", img );
Mat H[2]= {Mat(20,20,CV_8UC1),Mat(20,20,CV_8UC1)};
for (int i=0; i<200; i++)
for (int j=0; j<200; j++)
{
Vec3b intensity2 = img.at<Vec3b>(i ,j);
int blue = intensity2.val[0];
int green = intensity2.val[1];
int red = intensity2.val[2];
H[0].at<uchar>(i,j)=(blue+green+red)/3;
}
namedWindow( "Modify pixel", CV_WINDOW_AUTOSIZE );
imshow( "Modify pixel", H[0] );
waitKey(0);
return 0;
}
https://stackoverflow.com/a/19652248/4518710
What's your main purpose of creating an array of matrices?
Your code seems to have some issues,
First of all, you need to access coordinate of Mat NOT by hardcoding.
Use Mat::rows and Mat::cols instead.
original:
for (int i=0; i<200; i++)
for (int j=0; j<200; j++)
{
}
modified:
int rows = img.rows;
if(rows > 200)
rows = 200;
int cols = img.cols;
if(cols > 200)
cols = 200;
for (int i=0; i<rows; i++)
for (int j=0; j<cols; j++)
{
}
Second, use vector<> feature of C++ instead of array itself.
original:
Mat H[2]= {Mat(20,20,CV_8UC1),Mat(20,20,CV_8UC1)};
modified:
std::vector<Mat> H;
H.push_back(Mat(20, 20, CV_8UC1));
H.push_back(Mat(20, 20, CV_8UC1));
Third, directly casting int to uchar can cause potential side effects.
Use static_cast instead.
original:
H[0].at<uchar>(i,j)=(blue+green+red)/3;
modified:
H[0].at<uchar>(i,j) = static_cast<uchar>((blue+green+red)/3);
You might use an std::vector
Mat img;
img = imread("./res/mydhm.png", CV_LOAD_IMAGE_COLOR);
namedWindow("Original image", CV_WINDOW_AUTOSIZE);
imshow("Original image", img);
//Mat H[2] = { Mat(img.cols, img.row, img.type), Mat(img.cols, img.row, img.type) };
std::vector<Mat> H; // use vector
H.push_back(Mat(img.cols, img.rows, img.type()));
H.push_back(Mat(img.cols, img.rows, img.type()));
for (int i = 0; i<img.cols; i++)
{
for (int j = 0; j<img.rows; j++)
{
Vec3b intensity2 = img.at<Vec3b>(Point(i, j));
int blue = intensity2.val[0];
int green = intensity2.val[1];
int red = intensity2.val[2];
H[0].at<Vec3b>(Point(i, j)) = (uchar)(blue + green + red) / 3;
}
}
namedWindow("Modify pixel", CV_WINDOW_AUTOSIZE);
imshow("Modify pixel", H[0]);
waitKey(0);
don't forget to #include<vector>

from float array to mat , concatenate blocks of image

I have an image 800x800 which is broken down to 16 blocks of 200x200.
(you can see previous post here)
These blocks are : vector<Mat> subImages;
I want to use float pointers on them , so I am doing :
float *pdata = (float*)( subImages[ idxSubImage ].data );
1) Now, I want to be able to get again the same images/blocks, going from float array to Mat data.
int Idx = 0;
pdata = (float*)( subImages[ Idx ].data );
namedWindow( "Display window", WINDOW_AUTOSIZE );
for( int i = 0; i < OriginalImgSize.height - 4; i+= 200 )
{
for( int j = 0; j < OriginalImgSize.width - 4; j+= 200, Idx++ )
{
Mat mf( i,j, CV_32F, pdata + 200 );
imshow( "Display window", mf );
waitKey(0);
}
}
So , the problem is that I am receiving an
OpenCV Error: Assertion failed
in imshow.
2) How can I recombine all the blocks to obtain the original 800x800 image?
I tried something like:
int Idx = 0;
pdata = (float*)( subImages[ Idx ].data );
Mat big( 800,800,CV_32F );
for( int i = 0; i < OriginalImgSize.height - 4; i+= 200 )
{
for( int j = 0; j < OriginalImgSize.width - 4; j+= 200, Idx++ )
{
Mat mf( i,j, CV_32F, pdata + 200 );
Rect roi(j,i,200,200);
mf.copyTo( big(roi) );
}
}
imwrite( "testing" , big );
This gives me :
OpenCV Error: Assertion failed (!fixedSize()) in release
in mf.copyTo( big(roi) );.
First, you need to know where are your subimages into the big image. To do this, you can save the rect of each subimage into the vector<Rect> smallImageRois;
Then you can use pointers (keep in mind that subimages are not continuous), or simply use copyTo to the correct place:
Have a look:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat3b img = imread("path_to_image");
resize(img, img, Size(800, 800));
Mat grayImg;
cvtColor(img, grayImg, COLOR_BGR2GRAY);
grayImg.convertTo(grayImg, CV_32F);
int N = 4;
if (((grayImg.rows % N) != 0) || ((grayImg.cols % N) != 0))
{
// Error
return -1;
}
Size graySize = grayImg.size();
Size smallSize(grayImg.cols / N, grayImg.rows / N);
vector<Mat> smallImages;
vector<Rect> smallImageRois;
for (int i = 0; i < graySize.height; i += smallSize.height)
{
for (int j = 0; j < graySize.width; j += smallSize.width)
{
Rect rect = Rect(j, i, smallSize.width, smallSize.height);
smallImages.push_back(grayImg(rect));
smallImageRois.push_back(rect);
}
}
// Option 1. Using pointer to subimage data.
Mat big1(800, 800, CV_32F);
int big1step = big1.step1();
float* pbig1 = big1.ptr<float>(0);
for (int idx = 0; idx < smallImages.size(); ++idx)
{
float* pdata = (float*)smallImages[idx].data;
int step = smallImages[idx].step1();
Rect roi = smallImageRois[idx];
for (int i = 0; i < smallSize.height; ++i)
{
for (int j = 0; j < smallSize.width; ++j)
{
pbig1[(roi.y + i) * big1step + (roi.x + j)] = pdata[i * step + j];
}
}
}
// Option 2. USing copyTo
Mat big2(800, 800, CV_32F);
for (int idx = 0; idx < smallImages.size(); ++idx)
{
smallImages[idx].copyTo(big2(smallImageRois[idx]));
}
return 0;
}
For concatenating the sub-images into a single squared image, you can use the following function:
// Important: all patches should have exactly the same size
Mat concatPatches(vector<Mat> &patches) {
assert(patches.size() > 0);
// make it square
const int patch_width = patches[0].cols;
const int patch_height = patches[0].rows;
const int patch_stride = ceil(sqrt(patches.size()));
Mat image = Mat::zeros(patch_stride * patch_height, patch_stride * patch_width, patches[0].type());
for (size_t i = 0, iend = patches.size(); i < iend; i++) {
Mat &patch = patches[i];
const int offset_x = (i % patch_stride) * patch_width;
const int offset_y = (i / patch_stride) * patch_height;
// copy the patch to the output image
patch.copyTo(image(Rect(offset_x, offset_y, patch_width, patch_height)));
}
return image;
}
It takes a vector of sub-images (or patches as I refer them to) and concatenates them into a squared image. Example usage:
vector<Mat> patches;
vector<Scalar> colours = {Scalar(255, 0, 0), Scalar(0, 255, 0), Scalar(0, 0, 255)};
// fill vector with circles of different colours
for(int i = 0; i < 16; i++) {
Mat patch = Mat::zeros(100,100, CV_32FC3);
circle(patch, Point(50,50), 40, colours[i % 3], -1);
patches.push_back(patch);
}
Mat img = concatPatches(patches);
imshow("img", img);
waitKey();
Will produce the following image
print the values of i and j before creating Mat mf and I believe you will soon be able to find the error.
Hint 1: i and j will be 0 the first time
Hint 2: Use the copyTo() with a ROI like:
cv::Rect roi(0,0,200,200);
src.copyTo(dst(roi))
Edit:
Hint 3: Try not to do such pointer fiddling, you will get in trouble. Especially if you're ignoring the step (like you seem to do).

pass the pointer of image buffer to OpenCV and change the saturation?

I want to pass the pointer of my image buffer, change the saturation and see the result immediately. But the change is not applying in my buffer and it is not changing.
void changeSaturation(void* buffer,int width, int height)
{
Mat matObject(width, height, CV_8UC4, buffer);
m_matSource = matObject;
Mat newMat = m_matSource.clone();
// BGR to HSV
cvtColor(matSource, matSource, CV_BGR2HSV);
for(int i = 0; i < newMat.rows; ++i)
{
for(int j = 0; j < newMat.cols; ++j)
{
newMat.at<cv::Vec3b>(i, j)[1] = 255; //saturationValue;
}
}
// HSV back to BGR
cvtColor(newMat, m_matSource, CV_HSV2BGR); // here m_matSource->data change
}
How can I apply the change on my buffer?
I refactored your code when trying to reproduce your problem and in the process I fixed it. You cloned your source into newMat then changed the color space of your original image and then proceed to completely ignore your new modified image. Try this out:
void changeSaturation(Mat& image)
{
Mat result(image.rows, image.cols, image.type());
// BGR to HSV
cvtColor(image, result, CV_BGR2HSV);
for(int i = 0; i < result.rows; ++i)
{
for(int j = 0; j < result.cols; ++j)
result.at<cv::Vec3b>(i, j)[1] = 255; //saturationValue;
}
// HSV back to BGR
cvtColor(result, result, CV_HSV2BGR); // here m_matSource->data change
namedWindow("Original");
imshow("Original",image);
namedWindow("Duplicate");
imshow("Duplicate",result);
}
int main()
{
Mat image;
image = imread("C:/Users/Public/Pictures/Sample Pictures/Desert.jpg");
changeSaturation(image);
waitKey(0);
}
Edit
To modify the input image:
void changeSaturation(Mat& image)
{
// BGR to HSV
cvtColor(image, image, CV_BGR2HSV);
for(int i = 0; i < image.rows; ++i)
{
for(int j = 0; j < image.cols; ++j)
image.at<cv::Vec3b>(i, j)[1] = 255; //saturationValue;
}
// HSV back to BGR
cvtColor(image, image, CV_HSV2BGR); // here m_matSource->data change
}
Next Edit
This now has (almost) the original function signature:
void changeSaturation(uchar* buffer, int rows, int cols, int type)
{
Mat image(rows, cols, type, buffer);
Mat result;
// BGR to HSV
cvtColor(image, result, CV_BGR2HSV);
for(int i = 0; i < result.rows; ++i)
{
for(int j = 0; j < result.cols; ++j)
result.at<cv::Vec3b>(i, j)[1] = 255;
}
// HSV back to BGR
cvtColor(result, image, CV_HSV2BGR);
}
int main()
{
Mat image;
image = imread("C:/Users/Public/Pictures/Sample Pictures/Desert.jpg");
changeSaturation(image.data, image.rows, image.cols, image.type());
imshow("Original",image);
waitKey(0);
}
Your constructor Mat matObject(width, height, CV_8UC4, buffer); allocates matObject of size width and height at the location pointed by buffer.
In your function you are making changes to newMat, which is cloned from matObject. However, buffer doesn't point to newMat, it points to matObject and matObject is not changed by your function.