Opencv only process the parts of image - c++

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);

Related

Change RGB to GrayScale in c++

I am trying to convert RGB image to gray scale using average method. But the output that is get is different from the desired output. I'm taking the image and getting the rgb values. I perform average operation and store the averaged and another array of same size of the image. Finally i'm converting the array to Mat and displaying the image.
Input image:
Desired output:
My output:
int main()
{
Mat image;
image =imread("<image_path>");
int rows=image.rows;
int cols=image.cols;
int myArray[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
myArray[i][j] = 0;
}
}
uint8_t* pixelPtr = (uint8_t*)image.data;
int cn = image.channels();
Scalar_<uint8_t> bgrPixel;
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
bgrPixel.val[0] = pixelPtr[i*image.cols*cn + j*cn + 0]; // B
bgrPixel.val[1] = pixelPtr[i*image.cols*cn + j*cn + 1]; // G
bgrPixel.val[2] = pixelPtr[i*image.cols*cn + j*cn + 2]; // R
int average = (bgrPixel.val[0]+bgrPixel.val[1]+bgrPixel.val[2])/3;
myArray[i][j]=average;
}
}
Mat averaged_image(Size(rows, cols), CV_8UC3, myArray, Mat::AUTO_STEP);
imwrite("<path to save the image>",averaged_image);
imshow("averaged_image",averaged_image);
waitKey(0);
return 0;
}
When creating Mat averaged_image,
Mat averaged_image(Size(rows, cols), CV_8UC3, myArray, Mat::AUTO_STEP);
you need to use CV_32S not CV_8UC3 because your array element is not three chars, it's one 32-bit int.
You can also use the function cvtColor:
cv::Mat gray;
cv::cvtColor(image, gray, CV_BGR2GRAY);
Bonus: this function does correct weighting of the channels, because simple averaging may not be the right thing to do.

Set transparency of pixel by it's value in cv::Mat

I have two cv::Mat objects, one is CV_8UC1 and it's loaded from grayscale QImage:
QImage tmp = QImage(path/to/image);
setMap(cv::Mat(tmp.height(),
tmp.width(),
CV_8UC1,
const_cast<uchar *>(tmp.bits()),
static_cast<size_t>(tmp.bytesPerLine())
));
After I load it, I want to get every pixel value and change transparency of that pixel by its value and convert it to QImage. Currently, I access pixels like this:
for(int i = 0; i < getMap().rows; i++)
{
for(int j = 0; j < getMap().cols; j++){
uchar v = getMap().at<uchar>(i,j);
//qDebug() << v;
}
}
Now, I think I have only one choice - convert it to CV_8UC4 (or copy it somehow) and change it's alpha value, but I don't know how to copy/convert it by pixel. As I said, I need to change it's transparency by it's grayscale value.
I tried this, but when I did, program crashed
getMap().convertTo(requestedMap_, CV_8UC4);
for(int i = 0; i < getMap().rows; i++)
{
for(int j = 0; j < getMap().cols; j++){
uchar v = getMap().at<uchar>(i,j);
if(v < 50)
requestedMap_.at<cv::Vec4i>(i,j)[3] = 0;
}
}
How can I solve it?
Thanks for your help!

OpenCV GrabCut Mask

I have utilised the OpenCV GrabCut functionality to perform an image segmentation. When viewing the segmented image as per the code below, the segmentation is reasonable/correct. However, when looking at(at attempting to use) the segmrntation mask values, I am getting some very large numbers, and not the enumerated values one would expect from the cv::GrabCutClasses enum.
void doGrabCut(){
Vector2i imgDims = getImageDims();
//Wite image to OpenCV Mat.
const Vector4u *rgb = getRGB();
cv::Mat rgbMat(imgDims.height, imgDims.width, CV_8UC3);
for (int i = 0; i < imgDims.height; i++) {
for (int j = 0; j < imgDims.width; j++) {
int idx = i * imgDims.width + j;
rgbMat.ptr<cv::Vec3b>(i)[j][2] = rgb[idx].x;
rgbMat.ptr<cv::Vec3b>(i)[j][1] = rgb[idx].y;
rgbMat.ptr<cv::Vec3b>(i)[j][0] = rgb[idx].z;
}
}
//Do graph cut.
cv::Mat res, fgModel, bgModel;
cv::Rect bb(bb_begin.x, bb_begin.y, bb_end.x - bb_begin.x, bb_end.y - bb_begin.y);
cv::grabCut(rgbMat, res, bb, bgModel, fgModel, 10, cv::GC_INIT_WITH_RECT);
cv::compare(res, cv::GC_PR_FGD, res, cv::CMP_EQ);
//Write mask.
Vector4u *maskPtr = getMask();//uchar
for (int i = 0; i < imgDims.height; i++) {
for (int j = 0; j < imgDims.width; j++) {
cv::GrabCutClasses classification = res.at<cv::GrabCutClasses>(i, j);
int idx = i * imgDims.width + j;
std::cout << classification << std::endl;//Strange numbers here.
maskPtr[idx].x = (classification == cv::GC_PR_FGD) ? 255 : 0;//This always evaluates to 0.
}
}
cv::Mat foreground(rgbMat.size(), CV_8UC3, cv::Scalar(255, 255, 255));
rgbMat.copyTo(foreground, res);
cv::imshow("GC Output", foreground);
}
Why would one get numbers outside the enumeration when the segmentation is qualitatively correct?
I doubt on your //Write mask. step, why do you re-iterate the res and modify maskPtr as maskPtr[idx].x = (classification == cv::GC_PR_FGD) ? 255 : 0;, Basically you already have a single channel Binary image stored in the res variable, the cv::compare() returns a binary image
However if you still want to debug the values by iteration then you should use the standard technique for iterating a single channel image as:
for (int i = 0; i < m.rows; i++) {
for (int j = 0; j < m.cols; j++) {
uchar classification = res.at<uchar>(i, j);
std::cout << int(classification) << ", ";
}
}
As you are iterating a single channel mat you must use res.at<uchar>(i, j) and not res.at<cv::GrabCutClasses>.

OpenCV cvtColor modifies original image too

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).

OpenCV RGB compare

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.