Why do I get different values when using different datatypes when accessing pixels in a matrix? - c++

I have a single channel grayscale image (slice).
cout << "num" << slice.channels() << ends; //outputs 1
for(int x = 0;x<=slice.cols;x++){
for(int y = 0;y<=slice.rows;y++){
Vec3b currentPoint = slice.at<Vec3b>(x,y);
cout << currentPoint;
}
}
however, when I try to access a pixel and expect currentPoint to be a single int as it is a single channel image. However, i get [32, 36, 255] which is odd, as it implies three channels. I appreciate I am using a type that says vec3b, but even so, where is it getting the other two elements from?
So I replace Vec3b with uchar, then i get lots of \377. That is even more confusing.
Even when I do have a 3 channel image, I get odd outputs when trying to access a single element of Vec3b (i get more \377).
How can this make sense? I must be mis understanding how the at() method is used.
Firstly, how do I get a single output for each pixel (0-255)?
Also, where am I going wrong when i see \377?

A lot of stuff for a few lines of code...
Since your image is a grayscale image, you should access it with at<uchar>.
Pay attention that the at<> function accepts (rows, cols), which is the opposite of (x,y).
It's faster to scan by line, since the matrix is stored row-wise in memory.
To print out the value of a uchar, you need to cast to int, or you get the ASCII coded character.
The loops should not be <=, but instead <, or you go out of bounds.
So:
for(int y = 0; y < slice.rows; y++) {
for(int x = 0; x < slice.cols; x++) {
uchar currentPoint = slice.at<uchar>(y,x);
cout << int(currentPoint) << " ";
}
cout << "\n";
}

Related

How to reassign an individual element of a 2D parallel vector with a 1D vector?

Hi I am working on an assignment for my introduction to C++ class and I am completely stumped on a certain part. Basically the assignment is to open a file that contains individual integers (the data represents a grid of elevation averages), populate a 2D vector with those values, find the min and max value of the vector, convert each element of the vector to a 1D parallel vector containing the RGB representation of that value (in Grey scale), and export the data as a PPM file. I have successfully reached the point where I am supposed to convert the values of the vector to the RGB parallel vectors.
My issue is that I am not entirely sure how to assign the new RGB vector to the original element of the vector. Here is the code I have currently:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
int main () {
// initialize inputs
int rows;
int columns;
string fname;
// input options
cout << "Enter number of rows" << endl;
cin >> rows;
cout << "Enter number of columns" << endl;
cin >> columns;
cout << "Enter file name to load" << endl;
cin >> fname;
ifstream inputFS(fname);
// initialize variables
int variableIndex;
vector<vector<int>> dataVector (rows, vector<int> (columns));
int minVal = 0;
int maxVal = 0;
// if file is open, populate vector with data from file
if(inputFS.is_open()) {
for (int i = 0; i < dataVector.size(); i++) {
for (int j = 0; j < dataVector.at(0).size(); j++) {
inputFS >> variableIndex;
dataVector.at(i).at(j) = variableIndex;
}
}
}
// find max and min value within data set
for (int i = 0; i < dataVector.size(); i++) {
for (int j = 0; j < dataVector.at(0).size(); j++) {
if (dataVector.at(i).at(j) < minVal) {
minVal = dataVector.at(i).at(j);
}
if (dataVector.at(i).at(j) > minVal) {
maxVal = dataVector.at(i).at(j);
}
}
}
// initialize variables and new color vector
// -------PART I NEED HELP ON-----------
int range = maxVal - minVal;
int remainderCheck = 0;
double color = 0;
vector<int> colorVector = 3;
for (int i = 0; i < dataVector.size(); i++) {
for (int j = 0; j < dataVector.at(0).size(); j++) {
remainderCheck = dataVector.at(i).at(j) - minVal;
if (remainderCheck / range == 0) {
cout << "Color 0 error" << endl;
// still need to find the RGB value for these cases
}
else {
color = remainderCheck / range;
fill(colorVector.begin(),colorVector.end()+3,color);
dataVector.at(i).at(j) = colorVector; // <-- DOESN'T WORK
}
}
}
}
My knowledge with C++ is very limited so any help would be greatly appreciated. Also if you have any advice for the other comment dealing with the / operator issues in the same chunk of code, that too would also me incredibly appreciated.
Here are the actual instructions for this specific part:
Step 3 - Compute the color for each part of the map and store
The input data file contains the elevation value for each cell in the map. Now you need to compute the color (in a gray scale between white and black) to use to represent these evaluation values. The shade of gray should be scaled to the elevation of the map.
Traditionally, images are represented and displayed in electronic systems (such as TVs and computers) through the RGB color model, an additive color model in which red, green, and blue light are added together in various ways to reproduce a broad array of colors. In this model, colors are represented through three integers (R, G, and B) values between 0 and 255. For example, (0, 0, 255) represents blue and (255, 255, 0) represents yellow. In RGB color, if each of the three RGB values are the same, we get a shade of gray. Thus, there are 256 possible shades of gray from black (0,0,0) to middle gray (128,128,128), to white (255,255,255).
To make the shade of gray, you should use the min and max values in the 2D vector to scale each integer (elevation data) to a value between 0 and 255 inclusive. This can be done with the following equation:
color =(elevation - min elevation)(max elevation - min elevation) * 255
Check your math to ensure that you are scaling correctly. Check your code to make sure that your arithmetic operations are working as you want. Recall that if a and b are variables declared as integers, the expression a/b will be 0 if a==128 and b==256.
As you compute the shade of grey, store that value in three parallel vectors for R, G and B. Putting the same value for R, G and B will result in grey. The structure of the vector should mirror the vector with the elevation data.
Your professor is asking you to make three additional vector<vector<int>>s: 1 for each of R, G, and B. (I do not know why you need three separate vectors: they will have identical values, since for grayscale R==G==B for every element. Still, follow instructions.)
typedef std::vector <int> row_type;
typedef std::vector <row_type> image_type;
image_type dataVector( rows, row_type( columns ) );
image_type R ( rows, row_type( columns ) );
image_type G ( rows, row_type( columns ) );
image_type B ( rows, row_type( columns ) );
Also, be careful whenever you do something like fill(foo.begin(),foo.end()...). Attempting to fill beyond the end of the container (foo.end()+3) is undefined behavior.
Load your dataset into dataVector as before, find your min and max, then for each element find the grayscale value (in [0,255]). Assign that value to each corresponding element of R, G, and B.
Once you have those three square vectors, you can use them to create your PPM file.

Mat cells set to NULL in OpenCV?

Quick summary:
I create a cv::Mat by
cv::Mat m = cv::Mat::zeros(MAP_HEIGHT, MAP_WIDTH, CV_8UC1)
My approach after this is to see if i have any polygons in a list of polygons, and if i do, fill them in, and lastly i assign m to my public cv::Mat map (defined in the header-file).
What happens is basically:
cv::Mat m = cv::Mat::zeros(MAP_HEIGHT, MAP_WIDTH, CV_8UC1);
// possibly fill polygons with 1's. Nothing happens if there are no polygons
map = m;
The logic of my program is that position x,y is allowed if a 0 is occupying the cell. So no polygons => all map should be 'legit'.
I have defined this method to check whether a given x-y coordinate is allowed.
bool Map::isAllowed(bool res, int x, int y) {
unsigned char allowed = 0;
res = (map.ptr<unsigned char>(y)[x] == allowed);
}
Now the mystery begins.
cout << cv::countNonZero(map) << endl; // prints 0, meaning all cells are 0
for(int i = 0; i < MAP_HEIGHT; i++) {
unsigned char* c = map.ptr<unsigned char>(i);
for(int j = 0; j < MAP_WIDTH; j++) {
cout << c[j] << endl;
}
} // will print nothing, only outputs empty lines, followed by a newline.
If i print (c[j] == NULL) it prints 1.
If i print the entire Mat i see only 0's flashing over my screen, so they are clearly there.
Why does isAllowed(bool, x, y) return false for (0,0), when there is clearly a 0 there?
Let me know if any more information is needed, thanks!
Problem is solved now, here are my mistakes for future reference:
1: When printing, #Miki pointed out that unsigned characters -> ASCII value gets printed, not numerical representation.
2: in isAllowedPosition(bool res, int x, int y), res has a primitive type. Aka this is pushed on the stack and not a reference to a memorylocation. When writing to it, i write to the local copy and not to the one passed in as an argumet.
Two possible fixes, either pass in a pointer to a memorylocation and write to that, or simply return the result.
Since your data type is uchar (aka unsigned char), you're printing the ASCII value. Use
cout << int(c[j]) << endl;
to print the actual value.
Also map.ptr<unsigned char>(y)[x] can be rewritten simply as map.at<uchar>(y,x), or if you use Mat1b as map(y,x)

Write 2D array to PGM grayscale image

I'm creating various 2D arrays of sizes from 100x100 to 2000x2000 elements. The values within the arrays can be clamped down to 0 - 255 gray scale and then need to be written to a PGM image in order to visually represent the data.
For example, I'm declaring the arrays globally as:
element case1[100][100];
element is a structure of double pixelValue and a Boolean value (that won't be used when actually writing to the file but is necessary in the program).
In writing to the PGM image, I am having errors considering the FILE *fp in this area of the code when writing after the header:
int *p
for (int x = 0; x < dimension; x++)
{
for (int y = 0; y < dimension; y++)
{ //also doesn't work as: fp << (unsigned char)case1[x][y].pix;
int pix = case1[x][y].pixelValue;
*p = pix;
fp << (unsigned char)*p;
}
}
fclose(fp);
I'm unsure of how to work with the pointer in order to get the pixelValue from each location within the 2D array. I need to be able to iterate through each pixelValue to get the visual representation of the data.
Thank you for your help!
Used fputc() instead so that I could directly insert values instead of using pointers.

Opencv convolution matrix gives unusual results

So I have a program that is trying to apply a simple 3x3 convolution matrix to an image.
This is the function that is doing the work:
Mat process(Mat image) {
int x = 2;
int y = 2;
Mat nimage(image); //just a new mat to put the resulting image on
while (y < image.rows-2) {
while (x < image.cols-2) {
nimage.at<uchar>(y,x) = //apply matrix to pixel
image.at<char>(y-1,x-1)*matrix[0]+
image.at<char>(y-1,x)*matrix[1]+
image.at<char>(y-1,x+1)*matrix[2]+
image.at<char>(y,x-1)*matrix[3]+
image.at<char>(y,x)*matrix[4]+
image.at<char>(y,x+1)*matrix[5]+
image.at<char>(y+1,x-1)*matrix[6]+
image.at<char>(y+1,x)*matrix[7]+
image.at<char>(y+1,x+1)*matrix[8];
//if (total < 0) total = 0;
//if (total > 255) total = 255;
//cout << (int)total << ": " << x << "," << y << endl;
x++;
}
x = 0;
y++;
}
cout << "done" << endl;
return nimage;
}
And the matrix looks like this
double ar[9] = {-1,0,0,
0,2,0,
0,0,0};
And the image that is used as input looks like this:
The desired output (I ran the same matrix on the input image in GIMP):
And the result is... weird:
I think this has to do with the data type I use when I set a pixel of the new image (nimage.at<uchar>(y,x) = ...), because whenever I change it I get a different, yet still incorrect result.
From the OpenCV documentation about the copy constructor of Mat, emphasis mine:
m – Array that (as a whole or partly) is assigned to the constructed matrix. No data is copied by these constructors. Instead, the header pointing to m data or its sub-array is constructed and associated with it. The reference counter, if any, is incremented. So, when you modify the matrix formed using such a constructor, you also modify the corresponding elements of m. If you want to have an independent copy of the sub-array, use Mat::clone().
So
Mat nimage(image); //just a new mat to put the resulting image on
doesn't actually create a new matrix; it creates a new Mat object, but that object still refers to the same matrix. From then on nimage.at(y,x) acts like image.at(y,x).
To copy the image, use
Mat nimage(image.clone()); //just a new mat to put the resulting image on

Add 1 to vector<unsigned char> value - Histogram in C++

I guess it's such an easy question (I'm coming from Java), but I can't figure out how it works.
I simply want to increment an vector element by one. The reason for this is, that I want to compute a histogram out of image values. But whatever I try I just can accomplish to assign a value to the vector. But not to increment it by one!
This is my histogram function:
void histogram(unsigned char** image, int height,
int width, vector<unsigned char>& histogramArray) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
// histogramArray[1] = (int)histogramArray[1] + (int)1;
// add histogram position by one if greylevel occured
histogramArray[(int)image[i][j]]++;
}
}
// display output
for (int i = 0; i < 256; i++) {
cout << "Position: " << i << endl;
cout << "Histogram Value: " << (int)histogramArray[i] << endl;
}
}
But whatever I try to add one to the histogramArray position, it leads to just 0 in the output. I'm only allowed to assign concrete values like:
histogramArray[1] = 2;
Is there any simple and easy way? I though iterators are hopefully not necesarry at this point, because I know the exakt index position where I want to increment something.
EDIT:
I'm so sorry, I should have been more precise with my question, thank you for your help so far! The code above is working, but it shows a different mean value out of the histogram (difference of around 90) than it should. Also the histogram values are way different than in a graphic program - even though the image values are exactly the same! Thats why I investigated the function and found out if I set the histogram to zeros and then just try to increase one element, nothing happens! This is the commented code above:
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
histogramArray[1]++;
// add histogram position by one if greylevel occured
// histogramArray[(int)image[i][j]]++;
}
}
So the position 1 remains 0, instead of having the value height*width. Because of this, I think the correct calculation histogramArray[image[i][j]]++; is also not working properly.
Do you have any explanation for this? This was my main question, I'm sorry.
Just for completeness, this is my mean function for the histogram:
unsigned char meanHistogram(vector<unsigned char>& histogram) {
int allOccurences = 0;
int allValues = 0;
for (int i = 0; i < 256; i++) {
allOccurences += histogram[i] * i;
allValues += histogram[i];
}
return (allOccurences / (float) allValues) + 0.5f;
}
And I initialize the image like this:
unsigned char** image= new unsigned char*[width];
for (int i = 0; i < width; i++) {
image[i] = new unsigned char[height];
}
But there shouldn't be any problem with the initialization code, since all other computations work perfectly and I am able to manipulate and safe the original image. But it's true, that I should change width and height - since I had only square images it didn't matter so far.
The Histogram is created like this and then the function is called like that:
vector<unsigned char> histogramArray(256);
histogram(array, adaptedHeight, adaptedWidth, histogramArray);
So do you have any clue why this part histogramArray[1]++; don't increases my histogram? histogramArray[1] remains 0 all the time! histogramArray[1] = 2; is working perfectly. Also histogramArray[(int)image[i][j]]++; seems to calculate something, but as I said, I think it's wrongly calculating.
I appreciate any help very much! The reason why I used a 2D Array is simply because it is asked for. I like the 1D version also much more, because it's way simpler!
You see, the current problem in your code is not incrementing a value versus assigning to it; it's the way you index your image. The way you've written your histogram function and the image access part puts very fine restrictions on how you need to allocate your images for this code to work.
For example, assuming your histogram function is as you've written it above, none of these image allocation strategies will work: (I've used char instead of unsigned char for brevity.)
char image [width * height]; // Obvious; "char[]" != "char **"
char * image = new char [width * height]; // "char*" != "char **"
char image [height][width]; // Most surprisingly, this won't work either.
The reason why the third case won't work is tough to explain simply. Suffice it to say that a 2D array like this will not implicitly decay into a pointer to pointer, and if it did, it would be meaningless. Contrary to what you might read in some books or hear from some people, in C/C++, arrays and pointers are not the same thing!
Anyway, for your histogram function to work correctly, you have to allocate your image like this:
char** image = new char* [height];
for (int i = 0; i < height; ++i)
image[i] = new char [width];
Now you can fill the image, for example:
for (int i = 0; i < height; ++i)
for (int j = 0; j < width; ++j)
image[i][j] = rand() % 256; // Or whatever...
On an image allocated like this, you can call your histogram function and it will work. After you're done with this image, you have to free it like this:
for (int i = 0; i < height; ++i)
delete[] image[i];
delete[] image;
For now, that's enough about allocation. I'll come back to it later.
In addition to the above, it is vital to note the order of iteration over your image. The way you've written it, you iterate over your columns on the outside, and your inner loop walks over the rows. Most (all?) image file formats and many (most?) image processing applications I've seen do it the other way around. The memory allocations I've shown above also assume that the first index is for the row, and the second is for the column. I suggest you do this too, unless you've very good reasons not to.
No matter which layout you choose for your images (the recommended row-major, or your current column-major,) it is in issue that you should always keep in your mind and take notice of.
Now, on to my recommended way of allocating and accessing images and calculating histograms.
I suggest that you allocate and free images like this:
// Allocate:
char * image = new char [height * width];
// Free:
delete[] image;
That's it; no nasty (de)allocation loops, and every image is one contiguous block of memory. When you want to access row i and column j (note which is which) you do it like this:
image[i * width + j] = 42;
char x = image[i * width + j];
And you'd calculate the histogram like this:
void histogram (
unsigned char * image, int height, int width,
// Note that the elements here are pixel-counts, not colors!
vector<unsigned> & histogram
) {
// Make sure histogram has enough room; you can do this outside as well.
if (histogram.size() < 256)
histogram.resize (256, 0);
int pixels = height * width;
for (int i = 0; i < pixels; ++i)
histogram[image[i]]++;
}
I've eliminated the printing code, which should not be there anyway. Note that I've used a single loop to go through the whole image; this is another advantage of allocating a 1D array. Also, for this particular function, it doesn't matter whether your images are row-major or column major, since it doesn't matter in what order we go through the pixels; it only matters that we go through all the pixels and nothing more.
UPDATE: After the question update, I think all of the above discussion is moot and notwithstanding! I believe the problem could be in the declaration of the histogram vector. It should be a vector of unsigned ints, not single bytes. Your problem seems to be that the value of the vector elements seem to stay at zero when your simplify the code and increment just one element, and are off from the values they need to be when you run the actual code. Well, this could be a symptom of numeric wrap-around. If the number of pixels in your image are a a multiple of 256 (e.g. 32x32 or 1024x1024 image) then it is natural that the sum of their number would be 0 mod 256.
I've already alluded to this point in my original answer. If you read my implementation of the histogram function, you see in the signature that I've declared my vector as vector<unsigned> and have put a comment above it that says this victor counts pixels, so its data type should be suitable.
I guess I should have made it bolder and clearer! I hope this solves your problem.