Reading .bmp(24 bit) into a 2D array - c++

I'm a complete beginner to this.I'll try to explain myself as much as i can.
int i, j;
string filename;
cout << "Please enter the file name: " << endl;
cin >> filename;
fstream stream;
stream.open(filename.c_str(),
ios::in|ios::out|ios::binary);
int file_size = get_int(stream, 2);
int start = get_int(stream, 10);
int width = get_int(stream, 18);
int height = get_int(stream, 22);
This part should get the file and it's values.
for ( i = 0; i < height; i++ )
{
for ( j = 0; j < width; j++)
{
for (int k = 0; k < split*split; k++){
int pos = stream.tellg();
int blue = stream.get();
int green = stream.get();
int red = stream.get();
And this reaches inside each pixel and gets RBG values.
What i want is to first store RBG values into a 2D array and then do some manipulations on that array.Then i'd like to create a new file with manipulated image.
I've no clue so some instructions along with some code would be really helpfull.

Bmp file format is documented in many places. For example, on wikipedia.
The easiest way would be to implement structure that describes bmp header, and read entire structure in one go, then read individual pixels.
Your reading function is broken and doesn't function because you did not read file signature - "BM" field of the header.
On some operating system all there are already strcutures and functions for reading BMPs. On windows, there's BITMAPFILEHEADER. Using those structures means you aren't using "pure C++".
If you still want to read BMP yourself, read msdn articles aboud bmp or google for "read bmp file" tutorials.

This library is very easy to use http://easybmp.sourceforge.net/. U can easily check RGB values after loading the file.

Related

Why does imwrite on BMP image gets stuck / does not return?

I am reading a Bitmap-file from disk and write a copy back to disk after some manipulation, also writing a copy of the original file. The bitmaps are relatively small with a resolution of 31 x 31 pixel.
What I see is that when I have a resolution of 30 x 30 pixel then cv::imwrite correctly writes out the files, however if I go for a resolution of 31 x 31 pixel then cv:imwrite just gets stuck and does not return. This is happening on the same directories.
<...>
image = cv::imread(imageName, IMREAD_GRAYSCALE); // Read the file
if( image.empty() ) // Check for invalid input
{
cout << "Could not open or find the image" << std::endl ;
return -1;
}
Mat image_flip (width,height,CV_8U);
int8_t pixel_8b;
for (int i=0; i< width; i++){
for (int j=0; j < height; j++){
pixel_8b= image.at<int8_t>(i,j);
image_flip.at<int8_t>(width-i,j) = pixel_8b;
}
}
cout << "Writing files" << endl;
result=cv::imwrite("./output_flip.bmp", image_flip);
cout << result << endl;
return 0;
In the good case I get the file output_flip.bmp written to the disk and result is displayed. In the bad case of being stuck the last thing I see is "Writing files" and then nothing anymore. I can switch back and forth between the good and the bad case by just resizing the input image.
Any ideas how to solve that issue?
As already discussed in the comments, you didn't provide a minimal, reproducible example (MRE). So, I derived the following MRE from your code, because I wanted to point out several things (and wondered, how your code could work at all):
#include <opencv.hpp>
int main()
{
cv::Mat image = cv::imread("path/to/your/image.png", cv::IMREAD_GRAYSCALE);
// cv::resize(image, image, cv::Size(30, 30));
cv::Mat image_flip(image.size().height, image.size().width, CV_8U);
for (int i = 0; i < image.size().width; i++)
{
for (int j = 0; j < image.size().height; j++)
{
const uint8_t pixel_8b = image.at<uint8_t>(j, i);
image_flip.at<uint8_t>(j, image.size().width - 1 - i) = pixel_8b;
}
}
std::cout << "Writing files" << std::endl;
const bool result = cv::imwrite("./output_flip.bmp", image_flip);
std::cout << result << std::endl;
return 0;
}
For single channel, 8-bit image (CV_8U), use uint8_t when accessing single pixels.
When using .at, please notice, that the syntax is .at(y, x). For square images, it might be equal, but in general, it's a common source for errors.
Accessing .at(j, width-i) MUST fail for i = 0, if width = image.size().width, since the last index of image is width - 1.
After correcting these issues, I could run your code without problems for larger images, as well as resized images to 30 x 30 or 31 x 31. So, please have a look, if you can resolve your issue(s) by modifying your code accordingly.
(I'm aware, that the actual issue as stated in the question (hanging imwrite) is not addressed at all in my answer, but as I said, I couldn't even run the provided code in the first place...)
Hope that helps!

Can someone show me how to modify a txt file in c++ ? ( i`m working in codeblocks)

So i have a txt file like this:
3/1995 13,25,16,14
4/1995 36,1,24,48
5/1996 39,46,35,2
233/1996 14,16,25,12
And i want to modify it to look like this, in another txt file:
13,25,16,14
36,1,24,48
39,46,35,2
14,16,25,12
I also want to transform them from char to int and put them in a 2d vector.
This is what i tried so far:
#include<iostream>
#include<fstream>
#include<cstring>
using namespace std;
static const int WIDTH = 10;
static const int HEIGHT = 50;
int main()
{
char level[HEIGHT][WIDTH];
ifstream file;
file.open("new.txt");
for(int i = 0; i < HEIGHT; i++)
{
for(int j = 0; j < WIDTH; j++)
{
file>>level[i][j];
cout<<level[i][j];
}cout<<endl;
}
return 0;
}
And it doesn`t read the blank space so it just messes up everything.
In order to gather all characters including white spaces, I advise you to use "get" instead of the << operator.
EDIT : or getline as suggested
In order to apply to change you describe, it's quite easy as basically each line is made out of two string and you always want to keep the second one only.
So you'd just have to do, for every line, file >> "a string" >> "a string"; (you could actually use the same), and for the file you want the write the result : file2 << "a string"; (the second one you use if not always the same)
NOTE : there are probably better ways to do it, just the easier that came to my mind

transforming c++ array to matlab big matrix using file

In my code i'm changing my array (int*) and then I want to compare it into the matlab results.
since my array is big 1200 X 1000 element. this takes forever to load it into matlab
i'm trying to copy the printed output file into matlab command line...
for (int i = 0; i < _roiY1; i++)
{
for (int j = 0; j < newWidth; j++)
{
channel_gr[i*newWidth + j] = clipLevel;
}
}
ofstream myfile;
myfile.open("C:\\Users\\gdarmon\\Desktop\\OpenCVcliptop.txt");
for (int i = 0; i < newHeight ; i++)
{
for (int j = 0; j < newWidth; j++)
{
myfile << channel_gr[i * newWidth + j] << ", ";
}
myfile<<";" <<endl;
}
is there a faster way to create a readable matrix data from c++? into matlab?
The simplest answer is that it's much quicker to transfer the data in binary form, rather than - as suggested in the question - rendering to text and having Matlab parse it back to binary. You can achieve this by using fwrite() at the C/C++ end, and fread() at the Matlab end.
int* my_data = ...;
int my_data_count = ...;
FILE* fid = fopen('my_data_file', 'wb');
fwrite((void*)my_data, sizeof(int), my_data_count, fid);
fclose(fid);
In Matlab:
fid = fopen('my_data_file', 'r');
my_data = fread(fid, inf, '*int32');
fclose(fid);
It's maybe worth noting that you can call C/C++ functions from within Matlab, so depending on what you are doing that may be an easier architecture (look up "mex files").
Don't write the output as text.
Write your matrix into your output file the way Matlab likes to read: big array of binary.
ofstream myfile;
myfile.open("C:\\Users\\gdarmon\\Desktop\\OpenCVcliptop.txt", ofstream::app::binary);
myfile.write((char*) channel_gr, newHeight*newWidth*sizeof(channel_gr[0]));
You may want to play some games on output to get the array ordered column-row rather than row-column because of the way matlab likes to see data. I remember orders of magnitude improvements in performance when writing mex file plug-ins for file readers, but it's been a while since I've done it.

How do I read text into a 2D array of larger parameters than the text (2D array has more rows and columns) using object.get()?

I am attempting to read a text file of characters into a character array (char array[MAX_ROW][MAX_COL]) and the text file has less rows and columns than the character array. This leads to a problem of reading past the substance material that is within the text file. I run into this problem when I read in using the following method:
ifstream whiteboard; //ifstream object for reading from a file.
whiteboard.open(board_name.c_str());
for(int i = 0; i < MAXROW; i++){ //naive read for canvas, not considering the /n characters or the size of the image.txt
for(int j = 0; j < MAXCOL; j++){ //for this to store the print properly it must be the exact size of the image dimensions
canvas[i][j] = whiteboard.get();
if(whiteboard.get() == '/n'){
return;
}
else if(whiteboard.get() != '/n'){
canvas[i][j] = whiteboard.get();
}
}
}
whiteboard.close();
The code above is supposed to run through the 2d array only to the point where the '/n' character is. Thus, allowing me to enter an array of any size. The newline character is at the end of each line of text in a text file (in the form of an enter keystroke). The read to file stops when a newline has been read. However, I am having issues implementing this correctly. Does anyone have any hints that may help me see this more clearly? Thank you very much!
EDIT:
Input File
xxxxxxxxxxxxxx
x x
x x
x x
x x
x x
xxxxxxxxxxxxxx
I am hoping to input the box above (along with all of its ' ' characters contained within it) into corresponding values of the character array. I wish to stop the read after reading the rightmost column of x's in and after reading the final x (the bottom right). You can see that my problem comes from having an array that is larger than the size of my text box here. I understand I could fix this by equating the character array to have the same dimensions as the text box, but I wish to keep a large enough constant so that I can read in a file of a relatively large size.
I have one solution, do the following:
int n=1025, m=1024, MAX_LINES=1025, i=0, j=0;
char character, Matrix[n][m];
fstream file;
file.open("file.txt", ios::in);
while (file.get(character))// loop executes until the get() function is able to read contents or characters from the file
{
Matrix[n][m]=character; // copy the content of character to matrix[n][m], here n is used to work as lines and m as normal arrays.
m++; // finished copying to matrix[n][m] then m++ else the program will overwrite the contents to the same array.
If (m>=1024)// if string[n][m] reached the limit which is 1024.
{
Matrix[n][m]='\0'; //termimate that line
m=0;// start with a new column
if (n<MAX_LINES)// if n is less than 1024 then
n++;// write to a next line because m can support only 1024 chars.
}
Matrix[n][m]='\0';// this will terminate the whole string not just one line.
file.close();
The Matrix Arrays is filled until the file has contents, but less than 1024x1024 chars, u can increase the chars input from the file but i face problems when taking inn more than 4 kb of data into Matrix...
I hope this helps u, if helps even a bit then +1 vote to my answer pls...
You are looking for something like this:
ifstream whiteboard;
whiteboard.open(board_name);
if ( !whiteboard.is_open() )
{
// do something here...
}
char c;
int row = 0, col = 0;
while ( whiteboard.get( c ) )
{
if ( c == '\n' )
{
row++;
col = 0;
continue;
}
canvas[ row ][ col++ ] = *reinterpret_cast< unsigned char * >( &c );
}
whiteboard.close();
With this code you can fill up your matrix with the content of the file... always supposing the "array" in the file fits in the array canvas[][]
If you aren't restricted to using an array, I wanted to add an alternative solution that would allow your canvas to be dynamically sized.
using std::vector;
std::ifstream whiteboard(board_name);
vector<vector<char>> canvas;
canvas.emplace_back();
// ^ Replace with push_back(vector<char>()); if not using C++11
char c;
while (whiteboard.get(c)) {
if (c == '\n') {
canvas.emplace_back(); // See above
}
else {
canvas.back().push_back(c);
}
}
You can decide to iterate through your canvas by using either range-based for loops or by using an index (iterators work too but are ugly):
// Loop through using range-based for (C++11)
for (auto& row : canvas) {
for (auto& col : row) {
std::cout << col;
}
std::cout << std::endl;
}
// Loop through using index
for (int i = 0; i < canvas.size(); i++) {
for (int j = 0; j < canvas[i].size(); j++) {
std::cout << canvas[i][j];
}
std::cout << std::endl;
}

Convert LibSVM output to vector of floats

I need to form HOGDescriptor::setSVMDetector() input.
I compute descriptors with openCV then use libSVM to get model file.
To form input i know that i need to get support vectors' values and elementwise mul them with alphas (then add -rho at the end), but i don't get where to get these alphas.
I have a list of SVs like:
1 1:-0.0434783 2:0.153846 3:0.194444 4:-0.353712 5:-0.45054
1 1:-0.2173916 2:-0.38461 3:0.222262 4:-0.676686 5:-0.78062
but where to get alphas?
Ok, it seems things are clear now.
Alphas are the first column in my case.
Since i had all of them equal to -1 or 1 (dunno why) in my test model, i thought these were labels.
Anyway, here is my parser (but you need to leave only SVs in file):
std::ifstream ifs("cars_model.model");
const int nsv = 90;
const int nfeatures = 144;
float rho = 12.5459;
char ts[4000] = ""; // !
std::vector<float> res(nfeatures,0);
std::vector<float> alphas;
Mat_<float> temp(nsv, nfeatures);
int c = 0;
std::cout << "Loading model file...\n";
for (int i=0; i<nsv; i++) {
float al = 0;
ifs >> al;
alphas.push_back(al);
for (int j=0; j<nfeatures; j++) {
float ind, s;
char junk;
ifs >> ind >> junk >> s;
temp.at<float>(c, j) = s;
//std::cout << f << ' ' << s << '\n';
}
c++;
}
ifs.close();
std::cout << "Computing primal form...\n";
for (int i=0; i<nsv; i++) {
float alpha = alphas[i];
for (int j=0; j<nfeatures; j++) {
res[j] += (temp.at<float>(i,j) * alpha);
}
}
//res.push_back(-rho);
std::ofstream ofs("primal.txt");
for (int i=0; i<res.size(); i++)
ofs << res[i] << ' ';
ofs.close();
And you know, it works. You can set rho as threshold of the detector.
But why do you want to classify this "by hand"? OpenCv has a classification routine called predict, which uses found SVs' and alphas'
float response = SVM.predict(sampleMat);
If you really want to do it by yourself you would not only need SVs and alphas but also a kernel function used for training and comput
SUM alpha_i K( support_vector_i , data_point ) - rho
I am not sure whether it is possible to extract alphas "by hand" without extending the SVM class, as one can see in the sources - alphas are stored in the CvSVMDecisionFunc structure:
struct CvSVMDecisionFunc
{
double rho;
int sv_count;
double* alpha;
int* sv_index;
};
while the only reference to this structure is in the protected section:
protected:
(...)
CvSVMDecisionFunc* decision_func;
From the source code of svm.cpp we can find, that it is only publically accesible through the save routine. So some "hack" would be to save the model and extract alphas from there (it will be located in the "Decision function" section, written in human readable format).
The simplest extracion technique seems to extent the CvSVM class and include method like
public:
CvSVMDecisionFunc* get_decision_function() { return decision_func; }
update
after clarification, that OP actually is trying to use externaly trained model in opencv - the easiest way is to convert the libsvm model crated by other method (libsvm, linearsvm etc.) into opencv compatible format and load it using read method
void CvSVM::read( CvFileStorage* fs, CvFileNode* svm_node )
see source for more details.