Image processing in C++ - c++

I want to read a raw file, which has 3 interleaving and has a size of about(3.5MB) large into a three dimensional array.. the code that I am using to read the file is:
ifile.open(argv[2], ios::in|ios::binary);
for(int i=0; i<1278; i++){
for(int j=0; j<968; j++){
for(int k=0; k<3; k++){
Imagedata1[i][j][k]=ifile.get();
}
}
}
The thing is this array is not what i expect it to be.. I need the 1278 to be the width of the image.. the 968 to be the height and 3 bytes are the RGB values.. how should i write the code to read from the file such that the array gets populated correctly.. Thanks.

First, image files are usually stored, in order of smallest jump to largest, color values, column, row order. You are not reading them in that order.
ifile.open(argv[2], ios::in|ios::binary);
for(int j=0; j<968; j++){
for(int i=0; i<1278; i++){
for(int k=0; k<3; k++){
Imagedata1[i][j][k]=ifile.get();
}
}
}
That is how the loop should be arranged, though you may want to rename your variables to keep things straight:
ifile.open(argv[2], ios::in|ios::binary);
for(int row=0; row<968; row++){
for(int col=0; col<1278; col++){
for(int color=0; color<3; color++){
Imagedata1[col][row][color]=ifile.get();
}
}
}
Secondly, the way you allocate your array is really broken and inefficient. Here is how it should work:
#include <iostream>
#include <fstream>
class ColorValue {
public:
ColorValue(unsigned char r, unsigned char g, unsigned char b)
: r_(r), g_(g), b_(b) {}
ColorValue() : r_(0), g_(0), b_(0) {}
unsigned char getR() const { return r_; }
unsigned char getG() const { return g_; }
unsigned char getB() const { return b_; }
private:
unsigned char r_, g_, b_;
};
void readrows(const char *fname, ColorValue imagedata[][1278])
{
::std::ifstream ifile;
ifile.open(fname, ::std::ios::in|::std::ios::binary);
for (int row = 0; row < 968; ++row) {
for (int col = 0; col < 1278; ++col) {
char r, g, b;
ifile.get(r);
ifile.get(g);
ifile.get(b);
imagedata[row][col] = ColorValue(r, g, b);
}
}
}
int main(int argc, const char *argv[])
{
ColorValue (*imagedata)[1278] = new ColorValue[968][1278];
readrows(argv[1], imagedata);
delete[] imagedata;
}
Using a class for ColorValue keeps you from having magic indexes everywhere in your code for the 'r', 'g', and 'b' components. And allocating the array in this way keeps all the memory used for the image contiguous and removes several levels of unnecessary indirection. These both have the property of making your program much more cache friendly.
I also found a nice article that's a really comprehensive treatment of multi-dimensional arrays in C++.

C arrays don't work like that.
Let's say you wanted a 2D 1024*768 pixel buffer, with 4 bytes per pixel. You'd do something like this:
unsigned int pixbuf[1024*768];
for (int irow=0; irow < 1024; irow++)
for (int icol=0; icol < 768; icol++)
pixbuf[(irow*1024)+icol] = icolor;
Here is a good link that explains further:
http://c-faq.com/~scs/cclass/int/sx9.html
'Hope that helps!

As long as you always access your data through the three-dimensional array it does not matter how the structure of the array is laid down in memory. In your example you define the first index (i) as the column index, the (j) index as the row index and the (k) index as the pixel component index.
I suspect your image file has the data organized in the standard way, which is an array of rows, where each row is an array of pixels, where each pixel is an array of color components. If that is the case, then you can modify your code to work with that structure:
ifile.open(argv[2], ios::in|ios::binary);
for(int i=0; i<1278; i++){
for(int j=0; j<968; j++){
for(int k=0; k<3; k++){
Imagedata1[j][i][k]=ifile.get();
}
}
}
Note that I just swapped the i and j indexes in the body of the innermost for loop, the rest of the code is identical to yours.
If you also need to access the image data as a planar buffer, then you should reorganize your arrays so that the data in memory mimics the layout from the file. For this you want the first dimension to be the rows, then columns, then color components. That would end up like this:
unsigned char Imagedata1[968][1278][3];
ifile.open(argv[2], ios::in|ios::binary);
for(int i=0; i<968; i++){
for(int j=0; j<1278; j++){
for(int k=0; k<3; k++){
Imagedata1[i][j][k]=ifile.get();
}
}
}
Update: after a quick discussion in the comments, it seems you do want to read this in a more efficient way. If you want to load this image on the stack (probably not a good idea) you would declare your image buffer as:
unsigned char Imagedata1[968][1278][3];
I would instead put it on the heap, like this:
unsigned char* Imagedata1 = new unsigned char[968*1278*3];
Then you can read the data from the file with:
if (fread(Imagedata1, 1278*3, 968, fd) != 968) {
// handle error here
}
Note that the numbers that you put as count and size in the fread call are not that important, as long as those two numbers multiplied are equal to width*height*components.
If you then need to access this image as a 3-dimensional array, I recommend that you create an auxiliary function, something like this:
unsigned char* getPixel(int col, int row)
{
return Imagedata1 + row * 1278 * 3 + col * 3;
}
The return value of this function can be used as a one dimensional array of three elements, the red, green and blue of the pixel.
As a final suggestion, I recommend that you wrap your image in a class, and add member variables that define the width and height (and maybe the number of color planes as well). You do not want to hardcode the 1278s and the 968s all over the place in your code!
I hope this helps.

Related

Error in output of dynamically initialized 2D array

I'm working on a school project written in c++. I am trying to generate an array of rgb colours based on user input, for a program that generates fractals(irrelevant).
I found the need to use a pointer to a 2D array. The pointer has a global scope because it needs to be accessed across multiple files, in multiple functions.
There is no problem with the declaration of the pointer. It is as such:
in "global.h"
int **ptr;
int palette[10][3];
//all the statements like #ifndef GLOBAL_H_INCLUDED, etc are present
in main.cpp
extern int **ptr;
extern int palette[10][3];
void gen_array();
int main()
{
//initialize values of palette
//palette is basically list of colors that the user selects for the
//desired blend of colors to appear on the screen
gen_array();
}
in void gen_array(), in main.cpp
void gen_array()
{
int i=0, size=0, size_arr=0;
//size is a variable helpful in calculating "size_arr", which is the size of the dynamic array to be initialized
while(palette[i][0]!=256)
{ //calculates "size" }
for(int i=0; i<size-1; i++)
{
//to find "size_arr"
}
ptr= new int*[size_arr]();
for(int i = 0; i < size_arr; ++i)
{
ptr[i] = new int[3];
}
//the following piece of code enter's values column-wise into the array
int s=0;
for(int i=0; i<size-1; i++)
{
for(int k=0; k<3; k++)
{
int x=-1, a=0, b=0;
a= palette[i][k] - palette[i+1][k];
b= palette[i][k];
if(a<0)
{
a= -a;
x=1;
}
for(int j=0; j<=a; j++, s++)
{
ptr[i][k] = b;
//cout<<*(*(ptr+i)+k)<<' ';
b= b+ x;
}
b= b-x;
for(int j=a+1; j<=diff[i]; j++, s++)
{
ptr[i][k] = b;
cout<<ptr[i][k]<<' ';
}
cout<<endl;
}
}
//this output's the array that the pointer points to, on the screen
for(int i=0; i<size_arr; i++)
{
for(int j=0; j<3; j++)
{
cout<<ptr[i][j]<<' ';
}
cout<<endl;
}
}
The problem is that, the data is getting generated and inputted in the array correctly [in the second-last for loop], but when outputting it [the last for loop] I'm getting junk values.
The data inputted [the correct values] as obtained from the cout statements [that are now commented] is this [printed column-wise] whereas the O/P given out is this [printed row-wise].
If it is required to be know, "size" (in this case) =2, size_arr=256 and palette[0]={0, 0, 255} and palette[1]={0,255,0}.
Can anybody point out what the problem is, and whether it has something to do with the pointer initialization?
Even without reading all the program code like
for(int j=0; j<=a; j++, s++)
{
ptr[i][k] = b;
//cout<<*(*(ptr+i)+k)<<' ';
b= b+ x;
}
looks clearly wrong. At each iteration you are writing to ptr[i][k] and the loop doesn't change ptr, i or k. This means that it will keep writing to the very same location during the loop.
Look at your int i in the loop you're using to fill int** ptr. If size = 2 then you're never going to advance ptr far enough to fill the array. Consider filling the array like this:
for(int i = 0; i < size_arr; ++i){//for every pixel in the main array
for(int k = 0; k < 3; ++k){//for every subpixel
int my_value = ...;//calculate the value for this entry
*(*(ptr+i)+k) = my_value;//assign
Better yet, use a struct named pixel containing Uint8 r,g,b and allocate as pixel* ptr = new pixel[size] and iterate over pixels and assign ptr[i].r ptr[i].g ptr[i].b

How to turn the 3D array into 1D array?

In my code, I defined a 3D array to store the date on CUDA kernel.The code just like this:
if(k<2642){
double iCycle[100], jCycle[100];
int iCycleNum = 0, jCycleNum = 0;
for(double i=0; i<=1; i+=a, ++iCycleNum){
iCycle[iCycleNum] = i;
for(double j=0; j+i<=1; j+=c, ++jCycleNum){
jCycle[jCycleNum] = j;
[...]
r=(int)color[indexOutput];
g=(int)color[indexOutput+1];
b=(int)color[indexOutput+2];
d_RGB[k][iCycleNum][jCycleNum].x=r;//int3 (*d_RGB)[100][100]
d_RGB[k][iCycleNum][jCycleNum].y=g;
d_RGB[k][iCycleNum][jCycleNum].z=b;
}
}
}
In every cycle, there is an r,g,b. I want to store the r,g,b in d_RGB[k][iCycleNum][jCycleNum],then I need to pass them to the host. But in this case, every k has a different iCycleNum and jCycleNum, and I do not know the value of them, so the 3D array here is so awaste of space and may be it could bring some bugs. I wonder if it is a way to change the 3D array into a 1D one like this: d_RGB[k+iCycleNum*x+jCycleNum*x*y].
Sorry, my English is not so good to decribe it clearly, so if you can not get what I mean, please add a comment. Thank you.
Actually a "classic" 3D array is organized in the memory as a 1D array (since the memory is 1D oriented).
The Code:
int aiTest[5][5][5] = {0};
int* piTest = (int*)aiTest;
for (int i = 0; i < 125; i++)
{
piTest[i] = i;
}
does not make any memory violations - and the element aiTest[4][4][4] will have the value of 124.
So the answer: just cast it to the right type you need.
int arr[5][6][7];
int resultant_arr[210];
int count=0;
for(int i=0;i<5;i++)
{
for(int j=0;j<6;j++)
{
for(int k=0;k<7;k++)
{
if(count<210)
{
resultant_arr[count]=arr[i][j][k];
count++;
}
}
}
}

Passing 2d arrays without knowing the size?

Can anyone help me? I want to pass firstMatrix, secondMatrix, and finalMatrix. So far I have this. I could make it a global variable but I still wouldnt know the actual size
int matrixSummation(int firstMatrix[][matrixSize],int secondMatrix[][matrixSize], int finalMatrix[][matrixSize], int matrixSize){
for(int row=0; row< matrixSize; row++){
for(int col=0; col< matrixSize; col++){
finalMatrix[row][col]=firstMatrix[row][col]+secondMatrix[row][col];
}
}
}
int main(int argc, char** argv) {
int matrixSize;
cout << "Enter size of your matrices: " <<endl;
cin >> matrixSize;
int firstMatrix[matrixSize][matrixSize];
int secondMatrix[matrixSize][matrixSize];
int finalMatrix[matrixSize][matrixSize];
cout <<"Enter numbers for the 1st matrix: "<<endl;
for(int row = 0; row < matrixSize; row++){
for(int col=0; col< matrixSize; col++){
cin >> firstMatrix[row][col];
}
}
cout <<"Enter your numbers for the 2nd matrix: "<<endl;
for(int row = 0; row < matrixSize; row++){
for(int col=0; col< matrixSize; col++){
cin >> secondMatrix[row][col];
}
}
matrixSummation(firstMatrix,secondMatrix,finalMatrix,matrixSize);
}
You can use a function template:
template<std::size_t A, std::size_t B>
int matrixSummation(int (&firstMatrix)[A][B], int (&secondMatrix)[A][B], int (&finalMatrix)[A][B])
so that inside your function you can access the number of rows with A and the number of columns with B, without passing sizes around.
This can't be done. At compile time, the function
int matrixSummation(int firstMatrix[][matrixSize],int secondMatrix[][matrixSize], int finalMatrix[][matrixSize], int matrixSize){
should know the matrix dimesions (atleast the columns).
See here: Passing array with unknown size to function
You can make a 1D array represent your 2d array:
instead of
array[ROW][COL];
use
array[ROW*COL];
And when accessing:
a[r][c]; //r<5,c<5
Use:
a[r*ROW+c];
I have been working on dynamic array size for my image file as well, in the end i just pass the array using int**.
#include <iostream>
using namespace std;
void printMatrix(int** val, int row, int col)
{
int new_val[row][col];
int i, j;
for(i = 0; i < row; i++)
{
for(j = 0; j < col; j++)
{
new_val[i][j] = val[i][j];
cout<<new_val[i][j]<<" ";
}cout<<endl;
}
cout<<"test!"<<endl;
}
int main ()
{
int i, j, k;
int row, col;
row = 3;
col = 5;
int** array = new int*[row];
for(int i = 0; i < row; ++i)
{
array[i] = new int[col];
}
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
array[i][j] = k;
k++;
cout << array[i][j]<< " ";
}
cout<<endl;
}
cout<<endl;
printMatrix(array, row, col);
return 0;
}
The root of the problem is that this is not allowed in Standard C++:
cin >> matrixSize;
int firstMatrix[matrixSize][matrixSize];
Some compilers let this pass, so far; however you then run into difficulties when trying to use firstMatrix. For example the problem that you have had trying to pass it to a function.
In fact the reason this is not allowed in Standard C++ is that so far nobody has been able to come up with a proposal that does not run into difficulties sooner or later.
The best course of action would be to declare your arrays differently, in a way that is compatible with Standard C++.
In Standard C++, C-style arrays must have their dimension known at compile-time. Therefore you must use a different container.
The version that is the simplest code to write would look like:
vector< vector<int> > firstMatrix(matrixSize, vector<int>(matrixSize));
which creates a 2-D array with each dimension being matrixSize and then you can access it in the same way you would access a C-style array.
Importantly, you can pass firstMatrix to a function exactly the same way you would pass any other variable, and its sizes can be accessed via member functions of the vector, e.g. firstMatrix.size() is the number of rows, and firstMatrix[0].size() is the number of columns.
That version is a bit less efficient at run-time due to the fact that each row is stored in a separate allocation. To have a contiguous storage you can write:
vector<int> firstMatrix(matrixSize * matrixSize);
Then you simulate the rows and columns within that allocation, for example the previous code's firstMatrix[2][3] would in this solution be firstMatrix[2 * matrixSize + 3].
The big thing here is initializing a dynamically allocated 2-D array. I recommend using "vectors," but I show a pointer approach. There is no need to pass a set value for the size to the function.
int[][] matrixSummation(int firstMatrix[][],int secondMatrix[][]){
if(firstMatrix.length != secondMatrix.length
|| firstMatrix[].length !=secondMatrix[].length)
return NULL;
int **finalMatrix;
finalMatrix= new int*[firstMatrix.length];
for (int i = 0; i < 10; ++i) {
finalMatrix[i] = new int[firstMatrix.length];
//make sure you initialize each int[]__ with proper length.
}
for(int row=0; row< matrixSize; row++){
for(int col=0; col< matrixSize; col++){
finalMatrix[row][col]=firstMatrix[row][col]+secondMatrix[row][col];
}
}
return finalMatrix;
}

C++ create a variable number of arrays

I have read columns from a .txt file (the file has at the beginning the number of columns (nCol)) and put the values into an array (float values[nCol][nLin]).
Now, I want to copy the values (ex.: values[0][nLin], values[1][nLin]...) into different float arrays depending on the number of columns.
How can I crate float arrays for each column if the number of columns my change depending on the file I am reading?
//------ Declares Array for values ------//
const int nCol = countCols;
float values[nCol][nLin];
// Fill Array with '-1'
for(int c=0; c<nCol; c++){
for(int l=0; l<nLin; l++) {
values[c][l] = -1;
}
}
//------ Skips the reading of line of values file ------//
getline(inFile, dummyLine);
// reads file to end of *file*, not line
while(!inFile.eof()) {
for(int y=0; y<nLin; y++){
for (int i=0; i<nCol; i++) {
inFile >> values[i][y];
}
i=0;
}
}
const int nValues = countLines;
float Col1[nValues]=-1,
Col2[nValues]=-1,
Col3[nValues]=-1,
Col4[nValues]=-1,
Col5[nValues]=-1;
//------ Put values in specific Arrays ------//
for(int v=0; v<nValues; v++) {
Col1[v] = values[0][v];
Col2[v] = values[1][v];
Col3[v] = values[2][v];
Col4[v] = values[3][v];
Col5[v] = values[4][v];
}
cout << endl;
I want that float Col1[] to be from 1 to nCol, the last one to be float ColnCol[]
The best way IMO would be to use std::vector< std::vector<float> >
You do not need to make different 1D columns as you can manipulate this vector of vector as you want.
Instead you should use std::vector. It is a better choice for dynamic size allocation of a data type.

Simple issue with 2D arrays

So here is a function in a program that I am working on for class. IN the function, I want to combinations of a 8 character hand for 3,4,5,6,7 and 8 letter words that are validated with a checkDictionary function. Everything works fine and dandy but when I want to store these created words into a 2-d array of chars (basically a 1D array of strings) I get a segmentation fault error. The lines that are apparently causing these issues are the lines such as:
strcpy(arrWords[count],letters8);
strcpy(arrWords[count],letters7);
...
^ until letters3 ^
I just want to know what exactly is wrong with me storing into the 2d array with code lines such as those. Also if you could give me any suggestion on how to accurately store these created words into the 2d array (arrWords) it would be greatly appreciated. Thank you so much for your time! :D
miscellaneous:
this program is basically scrabble
the loops that increment i & j are used to increment through a 2d array board of 10x10 that is filled with "." and placed words by the user.
-the dictionary has 40438 actual words
void computerMove(char dictionary[][45], char arrBoard[11][11], char computer[9])
{
char let1, let2, let3, let4, let5, let6, let7, let8;
// char letters3[4];
// char letters4[5];
// char letters5[6];
// char letters6[7];
// char letters7[8];
// char letters8[9];
int count = 0;
char arrWords[40438][10];
for (int i=0; i<10; i++)
{
for (int j=0; j<10; j++)
{
// if (arrBoard[i][j]!='*')
let1=arrBoard[i][j];
for (int a=0; a<8; a++)
{
let2=computer[a];
for (int b=0; b<8; b++)
{
let3=computer[b];
char letters3[4]={let1,let2,let3};
cout<<letters3<<endl;
if (searchDictionary(dictionary,letters3,40438)==true)
strcpy(arrWords[count],letters3);
count++;
for (int c=0; c<8; c++)
{
let4=computer[c];
char letters4[5]={let1,let2,let3,let4};
if (searchDictionary(dictionary,letters4,40438)==true)
strcpy(arrWords[count],letters4);
count++;
for (int d=0; d<8; d++)
{
let5=computer[d];
char letters5[6]={let1,let2,let3,let4,let5};
if (searchDictionary(dictionary,letters5,40438)==true)
strcpy(arrWords[count],letters5);
count++;
for (int e=0; e<8; e++)
{
let6=computer[e];
char letters6[7]={let1,let2,let3,let4,let5,let6};
if (searchDictionary(dictionary,letters6,40438)==true)
strcpy(arrWords[count],letters6);
count++;
for (int f=0; f<8; f++)
{
let7=computer[f];
char letters7[8]={let1,let2,let3,let4,let5,let6,let7};
if (searchDictionary(dictionary,letters7,40438)==true)
strcpy(arrWords[count],letters7);
count++;
for (int g=0; g<8; g++)
{
let8=computer[g];
char letters8[9]={let1,let2,let3,let4,let5,let6,let7,let8};
if (searchDictionary(dictionary,letters8,40438)==true)
strcpy(arrWords[count],letters8);
count++;
}
}
}
}
}
}
}
}
// cout<<"YOURE OVER HERE PAL!"<<endl;
// for (int z=0; z<50; z++)
// cout<<arrWords[z]<<endl;
}
}
Thank you so much for trying to help!
strcpy is used for copying C-strings, not arrays.
This function looks for string ending null character '\0' for the source and destination inputs so that it knows where to stop copy operation. But in the case of array, it may not find this character for a long memory sequence and you get segmentation error.
Instead, you can use std::copy() like this:
std::copy(std::begin(letters3),std::end(letters3), arrWords[count] );