I have to create a program in c++ (standard libraries only) that detects edges with Sobel operator on a bmp photo and writes the result to the output bmp file (no need to create it, just to write into a previously created file). To do so I have to read an input bmp file and put it into a dynamic 2x2 array. Reading must be done in two different modes (picked up by an user) - first one is to read whole image into a memory in a dynamic array, second one is to read an image piece by piece (pieces with set size). I have no idea how to do a second mode, i guess it has to somehow read an exact number of lines from an input file, use a sobel on them and put them together in an output file, but i have no idea how to implement it. can you tell me how to start it?
this is some code that i've made, it's incomplete since i have to do a second mode but the first one is done (the Sobel operator code was taken from Dwayne Phillips' book about processing images in C, so it's more C-like)
#include <iostream>
#include <fstream>
using namespace std;
typedef unsigned char byte_t; //krotszy zapis
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////// BMP STRUCTS ////////////////////////////////////////////////////////////////////////////
struct BITMAPFILEHEADER {
byte_t bfType[2];
byte_t bfSize[4];
byte_t bfReserved1[2];
byte_t bfReserved2[2];
byte_t bfOffBits[4];
};
struct BITMAPINFOHEADER {
byte_t biSize[4];
byte_t biWidth[4];
byte_t biHeight[4];
byte_t biPlanes[2];
byte_t biBitCount[2];
byte_t biCompression[4];
byte_t biSizeImage[4];
byte_t biXpelsPerMeter[4];
byte_t biYpelsPerMeter[4];
byte_t biCrlUses[4];
byte_t biCrlImportant[4];
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////// SOBEL MASKS ////////////////////////////////////////////////////////////////////////////////////////
short mask_0[3][3] = { //S1
{ 1, 2, 1},
{ 0, 0, 0},
{-1, -2, -1} };
short mask_1[3][3] = { //S2
{ 2, 1, 0},
{ 1, 0, -1},
{ 0, -1, -2} };
short mask_2[3][3] = { //S3
{ 1, 0, -1},
{ 2, 0, -2},
{ 1, 0, -1} };
short mask_3[3][3] = { //S4
{ 0, -1, -2},
{ 1, 0, -1},
{ 2, 1, 0} };
short mask_4[3][3] = { //S5 (-S1)
{-1, -2, -1},
{ 0, 0, 0},
{ 1, 2, 1} };
short mask_5[3][3] = { //S6 (-S2)
{-2, -1, 0},
{-1, 0, 1},
{ 0, 1, 2} };
short mask_6[3][3] = { //S7 (-S3)
{-1, 0, 1},
{-2, 0, 2},
{-1, 0, 1} };
short mask_7[3][3] = { //S8 (-S4)
{ 0, 1, 2},
{-1, 0, 1},
{-2, -1, 0} };
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////// BASIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////////
int wordValue(byte_t* t) //value of 2 byte long variable
{
return t[0] + t[1] * 256;
}
int dwordValue(byte_t* t) //value of 4 byte long variable
{
return t[0] + t[1] * 256 + t[2] * 256 * 256 + t[3] * 256 * 256 * 256;
}
int menudisplay()
{
cout << "<------------------------------------- OPERATOR SOBELA - PROJEKT -------------------------------- >" << endl;
cout << "< Welcome at Sobel! >" << endl;
cout << "< Sobel detects edges in BMP files. >" << endl;
cout << "< It has two modes: >" << endl;
cout << "< 1. Reading whole image into a RAM >" << endl;
cout << "< 2. Reading an image piece by piece (for big files) >" << endl;
cout << "< To pick a mode, just enter 1 or 2. >" << endl;
cout << "< Then enter a name of an input file with .bmp. >" << endl;
cout << "< Later enter a name of an output value with .bmp. >" << endl;
cout << "< Have fun! >" << endl;
cout << "<----------------------- ----------------------->" << endl << endl;
cout << "Your choice: ";
}
void menuservice()
{
menudisplay();
powrot:
int a;
cin >> a;
switch (a)
{
case 1:
save_to_bmp();
break;
case 2:
/*pieces of image*/
break;
default:
cout << "Bad choice! Pick a mode once again: ";
goto powrot;
break;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////// BMP READING //////////////////////////////////////////////////////////////////////////////////
void readBFH(ifstream& ifs, BITMAPFILEHEADER& bfh) //reading bfh
{
ifs.read((char*)&(bfh.bfType), sizeof(bfh.bfType));
ifs.read((char*)&(bfh.bfSize), sizeof(bfh.bfSize));
ifs.read((char*)&(bfh.bfReserved1), sizeof(bfh.bfReserved1));
ifs.read((char*)&(bfh.bfReserved2), sizeof(bfh.bfReserved2));
ifs.read((char*)&(bfh.bfOffBits), sizeof(bfh.bfOffBits));
}
void readBIH(ifstream& ifs, BITMAPINFOHEADER& bih) //reading bih
{
ifs.read((char*)&(bih.biSize), sizeof(bih.biSize));
ifs.read((char*)&(bih.biWidth), sizeof(bih.biWidth));
ifs.read((char*)&(bih.biHeight), sizeof(bih.biHeight));
ifs.read((char*)&(bih.biPlanes), sizeof(bih.biPlanes));
ifs.read((char*)&(bih.biBitCount), sizeof(bih.biBitCount));
ifs.read((char*)&(bih.biCompression), sizeof(bih.biCompression));
ifs.read((char*)&(bih.biSizeImage), sizeof(bih.biSizeImage));
ifs.read((char*)&(bih.biXpelsPerMeter), sizeof(bih.biXpelsPerMeter));
ifs.read((char*)&(bih.biYpelsPerMeter), sizeof(bih.biYpelsPerMeter));
ifs.read((char*)&(bih.biCrlUses), sizeof(bih.biCrlUses));
ifs.read((char*)&(bih.biCrlImportant), sizeof(bih.biCrlImportant));
}
void writeBFH(ofstream& ofs, BITMAPFILEHEADER& bfh) //zapisywanie naglowka pliku
{
ofs.write((char*)&(bfh.bfType), sizeof(bfh.bfType));
ofs.write((char*)&(bfh.bfSize), sizeof(bfh.bfSize));
ofs.write((char*)&(bfh.bfReserved1), sizeof(bfh.bfReserved1));
ofs.write((char*)&(bfh.bfReserved2), sizeof(bfh.bfReserved2));
ofs.write((char*)&(bfh.bfOffBits), sizeof(bfh.bfOffBits));
}
void writeBIH(ofstream& ofs, BITMAPINFOHEADER& bih) //writing bih
{
ofs.write((char*)&(bih.biSize), sizeof(bih.biSize));
ofs.write((char*)&(bih.biWidth), sizeof(bih.biWidth));
ofs.write((char*)&(bih.biHeight), sizeof(bih.biHeight));
ofs.write((char*)&(bih.biPlanes), sizeof(bih.biPlanes));
ofs.write((char*)&(bih.biBitCount), sizeof(bih.biBitCount));
ofs.write((char*)&(bih.biCompression), sizeof(bih.biCompression));
ofs.write((char*)&(bih.biSizeImage), sizeof(bih.biSizeImage));
ofs.write((char*)&(bih.biXpelsPerMeter), sizeof(bih.biXpelsPerMeter));
ofs.write((char*)&(bih.biYpelsPerMeter), sizeof(bih.biYpelsPerMeter));
ofs.write((char*)&(bih.biCrlUses), sizeof(bih.biCrlUses));
ofs.write((char*)&(bih.biCrlImportant), sizeof(bih.biCrlImportant));
}
void displaydata(BITMAPFILEHEADER bfh, BITMAPINFOHEADER bih) //displaying data
{
cout << "Input file data:" << endl;
cout << "<---------------------------------------->";
cout << "File length: " << dwordValue(bfh.bfSize) << endl;
cout << "Header size: " << dwordValue(bih.biSize) << endl;
cout << "Image width: " << dwordValue(bih.biWidth) << endl;
cout << "Image height: " << dwordValue(bih.biHeight) << endl;
cout << "Image size: " << dwordValue(bih.biSizeImage) << endl;
cout << "Real amount of picture's bytes: " << dwordValue(bih.biSizeImage) - dwordValue(bfh.bfOffBits) << endl;
cout << "Zeros per row: " << zerosPerRow(bfh, bih) << endl;
}
int zerosPerRow(BITMAPFILEHEADER bfh, BITMAPINFOHEADER bih)
{
int realsize = dwordValue(bih.biSizeImage) - dwordValue(bfh.bfOffBits);
int height = dwordValue(bih.biHeight);
int width = dwordValue(bih.biWidth);
int zeros = 0;
if (width % 4 != 0)
{
int bytesPerRow = width * 3;
zeros = (bytesPerRow / 4 + 1) * 4 - bytesPerRow;
}
return zeros;
}
void save_to_bmp()
{
char infile[100];
char outfile[100];
cout << "Enter input file: ";
cin >> infile;
cout << "Enter output file: ";
cin >> outfile;
ifstream ifs(infile, ios::binary); //input stream
ofstream ofs(outfile, ios::binary); //output stream
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
if (!ifs.is_open() || !ofs.is_open())
{
cerr << "Opening error!";
save_to_bmp();
}
else {
readBFH(ifs, bfh);
readBIH(ifs, bih);
writeBFH(ofs, bfh);
writeBIH(ofs, bih);
displaydata(bfh, bih);
/*reading values of pixels*/
unsigned char p; //value of a single pixel
short** the_image; //input image array
the_image = new short* [dwordValue(bih.biWidth)];
for (int i = 0; i < dwordValue(bih.biWidth); i++)
{
the_image[i] = new short [dwordValue(bih.biHeight)];
}
short** out_image; //output image array
out_image = new short* [dwordValue(bih.biWidth)];
for (int i = 0; i < dwordValue(bih.biWidth); i++)
{
out_image[i] = new short[dwordValue(bih.biHeight)];
}
for (int i = 0; i < dwordValue(bih.biHeight); i++)
{
for (int j = 0; j < dwordValue(bih.biWidth); j++)
{
for (int n = 0; n < 2; n++)
{
ifs.read((char*)&p, sizeof(char));
ofs.write((char*)&p, sizeof(char));
}
the_image[i][j] = (short)p;
out_image[i][j] = (short)p;
}
}
/*zera*/
for (int i = 0; i < zerosPerRow(bfh, bih); i++)
{
ifs.read((char*)&p, sizeof(char));
ofs.write((char*)&p, sizeof(char));
}
sobel(the_image, out_image, dwordValue(bih.biWidth), dwordValue(bih.biHeight), dwordValue(bih.biBitCount));
for (int i = 0; i < dwordValue(bih.biWidth); i++)
{
delete[] the_image[i];
the_image[i] = NULL;
}
delete[] the_image;
the_image = NULL;
ifs.close();
ofs.close();
char a;
cout << "File has been read. Enter anything to exit program";
cin >> a;
system("cls");
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////// SOBEL CODE //////////////////////////////////////////////////////////////////////////////////////
short** image, ** out_image;
long rows, cols, bits_per_pixel;
int perform_convolution(short** image, short** out_image, long rows, long cols, long bits_per_pixel)
{
char response[80];
int a, b, i, is_present, j, sum;
short max, min, new_hi, new_low;
new_hi = 250;
new_low = 16;
if (bits_per_pixel == 4) {
new_hi = 10;
new_low = 3;
}
min = 0;
max = 255;
if (bits_per_pixel == 4)
max = 16;
/*cleaning an output array*/
for (i = 0; i < rows; i++)
for (j = 0; j < cols; j++)
out_image[i][j] = 0;
printf("\n ");
for (i = 1; i < rows - 1; i++) {
if ((i % 10) == 0) { printf("%4d", i); }
for (j = 1; j < cols - 1; j++) {
/*convolution*/
/*kierunek 0*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] *
mask_0[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
/*kierunek 1*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] * mask_1[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
/*kierunek 2*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] * mask_2[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
/*kierunek 3*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] * mask_3[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
/*kierunek 4*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] * mask_4[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
/*kierunek 5*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] * mask_5[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
/*kierunek 6*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] * mask_6[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
/*kierunek 7*/
sum = 0;
for (a = -1; a < 2; a++) {
for (b = -1; b < 2; b++) {
sum = sum + image[i + a][j + b] * mask_7[a + 1][b + 1];
}
}
if (sum > max) sum = max;
if (sum < 0) sum = 0;
if (sum > out_image[i][j])
out_image[i][j] = sum;
}
}
return(1);
}
int fix_edges(short** im, int w, long rows, long cols)
{
int i, j;
for (i = w; i > 0; i--) {
im[i - 1][i - 1] = im[i][i];
im[i - 1][cols - (i - 1)] = im[i][cols - 1 - (i - 1)];
im[rows - (i - 1)][i - 1] = im[rows - 1 - (i - 1)][i];
im[rows - (i - 1)][cols - (i - 1)] = im[rows - 1 - (i - 1)][cols - 1 - (i - 1)];
}
for (i = 0; i < rows; i++) {
for (j = w; j > 0; j--) {
im[i][j - 1] = im[i][j];
im[i][cols - j] = im[i][cols - j - 1];
}
}
for (j = 0; j < cols; j++) {
for (i = w; i > 0; i--) {
im[i - 1][j] = im[i][j];
im[rows - i][j] = im[rows - i - 1][j];
}
}
return(1);
}
int sobel(short** the_image, short** out_image, long rows, long cols, long bits_per_pixel) //wykrywanie krawedzi operatorem sobela
{
perform_convolution(the_image, out_image, rows, cols, bits_per_pixel);
fix_edges(out_image, 1, rows, cols);
return(1);
}
int main()
{
menuservice();
return 0;
}
Say I have a text file containing the 80bit hex number
0xabcdef0123456789abcd
My C++ program reads that using fstream into a char array called buffer.
But then I want to store it in a uint16_t array such that:
uint16_t * key = {0xabcd, 0xef01, 0x2345, 0x6789, 0xabcd}
I have tried several approaches, but I continue to get decimal integers, for instance:
const std::size_t strLength = strlen(buffer);
std::vector<uint16_t> arr16bit((strLength / 2) + 1);
for (std::size_t i = 0; i < strLength; ++i)
{
arr16bit[i / 2] <<= 8;
arr16bit[i / 2] |= buffer[i];
}
Yields:
arr16bit = {24930, 25444, 25958, 12337, 12851}
There must be an easy way to do this that I'm just not seeing.
Here is the full solution I came up with based on the comments:
int hex_char_to_int(char c) {
if (int(c) < 58) //numbers
return c - 48;
else if (int(c) < 91) //capital letters
return c - 65 + 10;
else if (int(c) < 123) //lower case letters
return c - 97 + 10;
}
uint16_t ints_to_int16(int i0, int i1, int i2, int i3) {
return (i3 * 16 * 16 * 16) + (i2 * 16 * 16) + (i1 * 16) + i0;
}
void readKey() {
const int bufferSize = 25;
char buffer[bufferSize] = { NULL };
ifstream* pStream = new ifstream("key.txt");
if (pStream->is_open() == true)
{
pStream->read(buffer, bufferSize);
}
cout << buffer << endl;
const size_t strLength = strlen(buffer);
int* hex_to_int = new int[strLength - 2];
for (int i = 2; i < strLength; i++) {
hex_to_int[i - 2] = hex_char_to_int(buffer[i]);
}
cout << endl;
uint16_t* key16 = new uint16_t[5];
int j = 0;
for (int i = 0; i < 5; i++) {
key16[i] = ints_to_int16(hex_to_int[j++], hex_to_int[j++], hex_to_int[j++], hex_to_int[j++]);
cout << "0x" << hex << key16[i] << " ";
}
cout << endl;
}
This outputs:
0xabcdef0123456789abcd
0xabcd 0xef01 0x2345 0x6789 0xabcd
essentially, I'm making a class that takes a BMP file for the constructor. From THIS POST I get all of the header data out of the way and then read RGB data into a vector. I then calculate the intensities as 0.25R + 0.5G + 0.25B. I put these numbers into a space-separated file, line by line. With the original above my result below and Using GNUPlot to open and plot the image gives me this result.
original
distortion
As you can see, the right side of the image is consistently being wrapped around further as the image is written to file (or somewhere before this process). I've pasted the code below, any help?
std::vector<char> MImage::readBMP(std::string const file){
static constexpr size_t HEADER_SIZE = 54;
std::ifstream bmp(file, std::ios::binary);
std::array<char, HEADER_SIZE> header;
bmp.read(header.data(), header.size());
auto fileSize = *reinterpret_cast<uint32_t*>(&header[2]);
auto dataOffset = *reinterpret_cast<uint32_t*>(&header[10]);
auto width = *reinterpret_cast<uint32_t*>(&header[18]);
auto height = *reinterpret_cast<uint32_t*>(&header[22]);
auto depth = *reinterpret_cast<uint16_t*>(&header[28]);
/*
std::cout << "fileSize: " << fileSize << std::endl;
std::cout << "dataOffset: " << dataOffset << std::endl;
std::cout << "width: " << width << std::endl;
std::cout << "height: " << height << std::endl;
std::cout << "depth: " << depth << "-bit" << std::endl;
*/
std::vector<char> img(dataOffset - HEADER_SIZE);
//bmp.read(img.data(), img.size());
auto dataSize = ((width * 3 + 3) & (~3)) * height;
img.resize(dataSize);
bmp.read(img.data(), img.size());
char temp = 0;
for (int i = dataSize - 4; i >= 0; i -= 3)
{
temp = img[i];
img[i] = img[i + 2];
img[i + 2] = temp;
}
// Convert to intensity
int k = 0;
int size = (int)img.size();
for (int j = 0; k+2 < size; j++)
{
//0.25B + 0.5G + 0.25R
img[j] = ((abs(img[k]) >> 2) + (abs(img[k + 1]) >> 1) + (abs(img[k + 2]) >> 2));
//OutputDebugStringA((to_string(img[j]) + "\n").c_str());
k += 3;
}
img.resize(dataSize / 3);
//OutputDebugStringA((to_string(img.size()) + "\n").c_str());
int a, b, c = 0;
//Testing #img data
ofstream TestPic;
TestPic.open("testpic.txt");
for (a = 0; a < HEIGHT; a++) {
for (b = 0; b < WIDTH; b++) {
TestPic << (int)img[c];
if (b < WIDTH-1) {
TestPic << " ";
}
c++;
}
TestPic << "\n";
}
TestPic.close();
return img; }
GNUPlot command: plot [0:630] [0:354] 'testpic.txt' matrix with image pixels
The problem you are seeing is caused by improper data alignment. Each scanline of .bmp file must have a byte-size divisible by 4. You are calculating the input data size correctly with this line:
auto dataSize = ((width * 3 + 3) & (~3)) * height;
However, while converting the img array you do not compensate/throw away the padding at the end of each scanline.
The best advice is either to use a standard bmp loader/converter like the one in STB libraries (the stb_image.h file) or, if you want to do it yourself, allocate the other array (img2 or something like that) and iterate img array scanline by scanline, writing greyscale values to img2.
By the way, this answer, which you have mentioned, contains a solution for your exact problem (it is called the "padding fix").
I followed the code at this link read pixel value in bmp file to be able to read the RGB values of pixels and when I have the entire image as one color and read a random pixel's values they are correct. After this I tried to make it so the function would also try and find how many unique colors there were so I added a box with a different color to the image but the function still only finds one color. I'm wondering if maybe I'm somehow not looking at all the bytes contained in the BMP but I'm not sure how that would be as I'm new to trying this stuff.
To make sure the code wasn't finding different colored pixels but failing to add them to the list of unique pixels I tried printing output when a color is found that is different from the one that is always found but no output ever came from it.
struct Color {
int R = -1;
int G = -1;
int B = -1;
};
unsigned char* readBMP(char* filename) {
int i;
FILE* f = fopen(filename, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f);
int width = *(int*)&info[18]; //the reason *(int*) is used here because there's an integer stored at 18 in the array that indicates how wide the BMP is
int height = *(int*)&info[22]; // same reasoning for *(int*)
int size = 3 * width * height;
unsigned char* data = new unsigned char[size];
fread(data, sizeof(unsigned char), size, f);
fclose(f);
// windows has BMP saved as BGR tuples and this switches it to RGB
for(i = 0; i < size; i += 3){
unsigned char tmp = data[i];
data[i] = data[i+2];
data[i+2] = tmp;
}
i = 0; // i is the x value of the pixel that is having its RGB values checked
int j = 0; // j is the y value of the pixel that is having its RGB values checked
unsigned char R = data[3 * (i * width + j)]; // value of R of the pixel at (i,j)
unsigned char G = data[3 * (i * width + j) + 1]; // value of G of the pixel at (i,j)
unsigned char B = data[3 * (i * width + j) + 2]; // value of B of the pixel at (i,j)
std::cout << "value of R is " << int(R);
std::cout << " value of G is " << int(G);
std::cout << " value of B is " << int(B);
Color num_colors[5];
int count;
int z;
int flag;
int iterator;
int sum;
for(count = 0; count < size; count += 1){
unsigned char R = data[3 * (i * width + j)];
unsigned char G = data[3 * (i * width + j) + 1];
unsigned char B = data[3 * (i * width + j) + 2];
sum = int(R) + int(G) + int(B);
if(sum != 301) {// 301 is the sum of the RGB values of the color that the program does manage to find
std::cout << sum;
}
flag = 0;
for(z = 0; z < 5; z += 1){
if(num_colors[z].R == R && num_colors[z].G == G && num_colors[z].B == B){
flag = 1;
}
}
if(flag == 1){
continue;
}
iterator = 0;
while(num_colors[iterator].R != -1){
iterator += 1;
}
num_colors[iterator].R = R;
num_colors[iterator].G = G;
num_colors[iterator].B = B;
}
int number = 0;
for(int r = 0; r < 5; r += 1){
std::cout << "\nValue of R here: " << num_colors[r].R;
if(num_colors[r].R != -1){
number += 1;
}
}
std::cout << "\nNumber of colors in image: " << number;
return data;
}
https://imgur.com/a/dXllIWL
This is the picture I'm using so there should be two colors found but the code only finds red pixels.
Your problem is that you are always checking the RGB values at (0,0)
i = 0; // i is the x value of the pixel that is having its RGB values checked
int j = 0; // j is the y value of the pixel that is having its RGB values checked
...
for(count = 0; count < size; count += 1){
unsigned char R = data[3 * (i * width + j)];
unsigned char G = data[3 * (i * width + j) + 1];
unsigned char B = data[3 * (i * width + j) + 2];
i and j defines the X and Y position of the pixel you are checking, but notice that you never change those in the loop. Your loop will keep doing the same thing over and over again. What you probably want is a double loop, going through all coordinates in your image:
for(int y=0; y<height; y++)
for(int x=0; x<width; x++){
unsigned char R = data[3 * (y * width + x) + 0];
unsigned char G = data[3 * (y * width + x) + 1];
unsigned char B = data[3 * (y * width + x) + 2];
I want to read and write a ppm image and calculate the average number of color.
My read method works fine and also the average number of each color is correct, but my write method gives me a wrong result.
The buffer which holds the data has type Vec3<float>*. Every 3D vector holds the r, g and b values.
Here is the read method:
Image * ReadPPM(const char * filename) {
Image* image;
ifstream file;
unsigned int width, height;
string version;
float maxvalue;
file.open(filename, ios::in | ios::binary);
if (!file) {
cerr << "file could not be open" << endl;
exit(EXIT_FAILURE);
}
// read the header
file >> version;
// check the header
// PPM "header" is valid
if (version.compare("P6") != 0)
{
cout << "Invalid image format (must be 'P6')";
exit(EXIT_FAILURE);
}
file >> width >> height >> maxvalue;
size_t size = height * width*3;
unsigned char * buffer = new unsigned char[size];
Vec3<float>* finalBuffer = new Vec3<float>[size / 3];
file.get();
file.read( (char *)buffer, size);
int j = 0;
for (int i = 0; i < size; i+=3) {
Vec3<float> vec(
(int)buffer[i]/maxvalue,
(int)buffer[i+1] / maxvalue,
(int)buffer[i+2] / maxvalue
);
finalBuffer[j] = vec;
j++;
}
file.clear();
file.close();
image = new Image(width, height, finalBuffer);
return image;
}
Here is the write method:
bool Image::operator >> (string filename) {
ofstream file;
file.open(filename,ios::out | ios::binary);
if (!file) {
cerr << "Cannot open file" << endl;
return false;
}
file << "P6" <<"\n";
file << getWidth() << "\n";
file << getHeight() << "\n";
file << "255" << "\n";
size_t size = height * width;
void*img = getRawDataPtr(); //returns void* buffer
unsigned char * temp = new unsigned char[size*3];
Vec3<float>* buff = (Vec3<float>*)img;
for (int i = 0; i<size; i++) {
Vec3<float> vector;
vector[0] =buff[i].x;
vector[1] = buff[i].y;
vector[2] = buff[i].z; // /255.f;
buff[i] = vector;
}
for (int i = 0; i < size; i++) {
temp[i * 3] = static_cast<unsigned char>(buff[i].x);
temp[i * 3 + 1] = static_cast<unsigned char>(buff[i].y);
temp[i * 3 + 2] = static_cast<unsigned char>(buff[i].z);
}
file.write((char *)temp, size * 3);
if (file.fail()) {
cerr << "Could not write data" << endl;
return false;
}
file.clear();
file.close();
return true;
delete[] buff;
delete[] temp;
}
Your values are normalized between 0 and 1 when reading the file. When writing, you just cast the float values into unsigned char, so you can only obtain 0 and 1 values: I guess you final image is filled with black.
void*img = getRawDataPtr(); //returns void* buffer
unsigned char * temp = new unsigned char[size*3];
Vec3<float>* buff = (Vec3<float>*)img;
Why creating a dynamic array you will have to manage? You are coding in C++, so use a container like std::vector.
for (int i = 0; i<size; i++) {
Vec3<float> vector;
vector[0] =buff[i].x;
vector[1] = buff[i].y;
vector[2] = buff[i].z; // /255.f;
buff[i] = vector;
}
What is the purpose of this loop? You are creating a Vec3<float> filled without transformation from the Vec3<float> in the current pixel in which you assign without transformation your temporary value: this just does nothing.
return true;
delete[] buff;
delete[] temp;
As I said in the comments, the code after return is not executed, so you will never release the memory you allocated for temp. Use a std::vector and you will not have to do it. buff is your original image: you should not delete it here.
Once cleaned, your code may become:
std::vector<unsigned char> temp(size*3);
Vec3<float>* buff = static_cast<Vec3<float>*>(getRawDataPtr());
for (int i = 0; i < size; i++) {
temp[i * 3] = static_cast<unsigned char>(buff[i].x * 255);
temp[i * 3 + 1] = static_cast<unsigned char>(buff[i].y * 255);
temp[i * 3 + 2] = static_cast<unsigned char>(buff[i].z * 255);
}
file.write(reinterpret_cast<char*>(&temp[0]), size * 3);
if (file.fail()) {
cerr << "Could not write data" << endl;
return false;
}
file.close();
return true;