Question

Since i am quiet new to C++ and Image processing i have a problem modifying and adding a function to the code. The requirement is only to switch between the RGB colors.

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "math.h"

class myImageData
{
private:
    int mW;
    int mH;
    int mCH;
    double * mData;

    void SkipComments(FILE *fp)
    {
        int ch;
        char line[100];

        while ((ch = fgetc(fp)) != EOF && isspace(ch))
            ;

        if (ch == '#')
        {
            fgets(line, sizeof(line), fp);
            SkipComments(fp);
        }
        else
        {
            fseek(fp, -1, SEEK_CUR);
        }
    }

public:

    myImageData(void)
    {
        this->mData = NULL;
    }

    ~myImageData()
    {
        if (this->mData != NULL)
        {
            delete[] this->mData;
        }
    }

    void init(int W, int H, int CH)
    {
        this->mW = W;
        this->mH = H;
        this->mCH = CH;

        if (this->mData != NULL)
            delete[] this->mData;

        this->mData = new double[(this->mW)*(this->mH)*(this->mCH)];
    }

    int getWidth(void)
    {
        return this->mW;
    }

    int getHeight(void)
    {
        return this->mH;
    }

    int getCH(void)
    {
        return this->mCH;
    }

    double * getDataPtr(void)
    {
        return this->mData;
    }

    double get(int x, int y)
    {
        return this->mData[y*(this->mW) + x];
    }

    double get(int x, int y, int CH)
    {
        return this->mData[this->mCH * (y*(this->mW) + x) + CH];
    }

    void set(int x, int y, double value)
    {
        this->mData[y*(this->mW) + x] = value;
    }

    void set(int x, int y, int CH, double value)
    {
        this->mData[this->mCH *(y*(this->mW) + x) + CH] = value;
    }

    void read(const char *filename);
    void save(const char *filename);
};


void myImageData::read(const char *filename)
{
    FILE *file = fopen(filename, "r");
    if (file == NULL){
        printf("Cannot open %s\n", filename);
        exit(1); //abnormal termination
    }
    printf("Read an image from: %s\n", filename);

    // read ppm/pgm header

    char buf[256];
    char filetype[256];
    int W, H, Range, CH;

    fgets(buf, sizeof(buf), file);
    sscanf(buf, "%s", filetype);

    SkipComments(file);
    fgets(buf, sizeof(buf), file);
    sscanf(buf, "%d%d", &W, &H);

    SkipComments(file);
    fgets(buf, sizeof(buf), file);
    sscanf(buf, "%d", &Range);
    //printf("Header: %s, %d, %d, %d\n", filetype, W, H, Range);

    SkipComments(file);

    if (strcmp(filetype, "P5") == 0)
    {
        CH = 1;
    }
    else if (strcmp(filetype, "P6") == 0)
    {
        CH = 3;
    }
    else
    {
        printf("Unknown image type\n");
        exit(1); //abnormal termination
    }

    if (Range != 255){
        printf("Invalid data\n");
        exit(1); //abnormal termination
    }

    // create myImageData class

    init(W, H, CH);

    // read ppm data

    int datalength = this->mW * this->mH * this->mCH;
    unsigned char * temp = new unsigned char[datalength];
    fread(temp, sizeof(unsigned char), datalength, file);

    double * ptr1 = this->mData;
    unsigned char *ptr2 = temp;

    for (int i = 0; i < datalength; i++){
        *ptr1 = (double)*ptr2;
        ptr1++;
        ptr2++;
    }

    delete[] temp;

    fclose(file);

}

void myImageData::save(const char *filename){

    char filenamefull[256];
    if (this->mCH == 1){
        sprintf(filenamefull, "%s.pgm", filename);
    }
    else{
        sprintf(filenamefull, "%s.ppm", filename);
    }

    FILE *file = fopen(filenamefull, "w");
    printf("Write an image to: %s \n", filenamefull);

    if (this->mCH == 1){
        fprintf(file, "P5\n");
    }
    else{
        fprintf(file, "P6\n");
    }

    fprintf(file, "%d %d\n", this->mW, this->mH);
    fprintf(file, "255\n");

    int datalength = this->mW * this->mH * this->mCH;
    unsigned char * temp = new unsigned char[datalength];

    double * ptr1 = this->mData;
    unsigned char * ptr2 = temp;

    for (int i = 0; i < datalength; i++){
        double value = *ptr1;
        value = round(value);
        if (value > 255) value = 255;
        if (value < 0) value = 0;
        *ptr2 = (unsigned char)value;
        ptr1++;
        ptr2++;
    }

    fwrite(temp, sizeof(unsigned char), datalength, file);
    delete[] temp;

    fprintf(file, "Â¥n");

    fclose(file);
}

The errors i am having:
error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup
error LNK1120: 1 unresolved externals

Was it helpful?

Solution

Firstly, you have no main function. No surprise that your code doesn't work.. all you have is a class to load, save and manipulate PPM image files.

You appear to me using Visual Studio, so you'll need a function that looks like this:

int _tmain(int argc, _TCHAR* argv[])
{
    myImageData image;
    image.read("atestfile.ppm");

    // do some stuff to your image

    image.write("outputfile.ppm");
}

I'm assuming you have a test image in PPM format you can use here, of course.

Now this is madness:

double * ptr1 = this->mData;
unsigned char * ptr2 = temp;

for (int i = 0; i < datalength; i++){
    double value = *ptr1;
    value = round(value);
    if (value > 255) value = 255;
    if (value < 0) value = 0;
    *ptr2 = (unsigned char)value;
    ptr1++;
    ptr2++;
}

You've read from an unsigned char, so there's no point stuffing it into a double, and there's definitely no point in checking if the value lies outside of 0 to 255. Why are you storing things in doubles? It makes no sense! Even if you did do something that needed a full double-precision floating point value per channel, you throw it all away in the output when you clamp everything to 0-255 again:

for (int i = 0; i < datalength; i++){
    double value = *ptr1;
    value = round(value);
    if (value > 255) value = 255;
    if (value < 0) value = 0;
    *ptr2 = (unsigned char)value;
    ptr1++;
    ptr2++;
}

Also, this is basically C dressed up in a thin C++ veneer. That's okay, everyone has to start somewhere. But instead of using new to create an array, you can do this:

// read ppm data

int datalength = this->mW * this->mH * this->mCH;

// using a std::vector here means that the allocated memory will be freed
// automatically, even in the result of an error occurring.
std::vector<unsigned char> temp(datalength);
fread(&temp[0], sizeof(unsigned char), datalength, file);

I'd also consider using iostream classes such as fstream instead of fread and fopen and so on. But this is not really the place to get into those kinds of details.

Anyway, What to do with your image once it is loaded? You've got dead easy helper functions to read and write pixel values, which will let you do pretty much anything you want. Here's a simple example, swapping the R and B channels. You might get something better when you actually tell us what you wanted.

void swapRB(myImageData& image)
{
    assert(image.getCH() == 3);

    for (int x = 0; x < image.getWidth())
    {
        for (int y = 0; x < image.getHeight())
        {
            double R = image.get(x, y, 0);
            double G = image.get(x, y, 1);
            double B = image.get(x, y, 2);

            image.set(x, y, 0, B);
            image.set(x, y, 2, R);
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top