user1984300 user1984300 - 1 month ago 19
C++ Question

Resize volume file length and width in C++

I have a volume file that has a variable slice length, image height of 640, and image width of 512. I want to convert it to a file with a height of 620 and a width of 420 and write it to a new file. I want to take the 20 pixels off the top of the height and take 46 off of each side of the width to do this resizing. Each pixel is unsigned 16 bits. I'm not sure how to do the actual manipulation.

This is what I have in my main:

FILE* pfile = NULL;
FILE* pfile2 = NULL;

pFile = fopen("test.vol", "rb");
fseek (pFile , 0 , SEEK_END);

int fileData = ftell(pFile);
rewind(pFile);

char* FileBuffer = new char[fileData];
fread(FileBuffer, fileData, 1, pFile);

int height = 640;
int width = 512;
int slizeSize = height * width * 2;
int sliceCount = fileData / sliceSize;
uint16_t pixels;

int newHeight = height - 20;
int newWidth = width - 92;
int newSliceSize = newHeight * newWidth * 2;
int newImageSize = newSliceSize * sliceCount;

char* NewFileBuffer = new char[newImageSize];

for (int i = 0; i < newHeight; i++) {

for (int i = 0; i < newWidth; i++) {

}
// need help in these for loops and after

}

fclose (pFile);
free (FileBuffer);

pFile2 = fopen("test2.vol", "wb");
fwrite(NewFileBuffer, NewImageSize, 1, pFile2);

Answer

If you're programming in C++, use C++: get rid of FILE*and other C stuff.

You're allocating an array of char with new, but call free to release memory: you should call delete[], or even better you should use std::vector and let it manage memory for you.

You're dealing with 16 bits values, so why not reading/writing/processing 16 bits values?

First, read the file:

#include <fstream>
#include <iterator>
#include <vector>

int main()
{
    using data_type = uint16_t;

    std::basic_ifstream<data_type> file_in{
        "test.vol",
        std::ifstream::binary
    };
    std::vector<data_type> FileBuffer{
        std::istreambuf_iterator<data_type>(file_in), 
        std::istreambuf_iterator<data_type>() // end of stream iterator
    };

Now FileBuffer is an array of uint16_t filled with the content of "test.vol", with managed memory allocation and deallocation.

Next part, resize the volume. I assume your data are packed in the following order: columns, lines, slices. It is unclear if the top are the first, or the last lines of a slice. Here is my proposition using iterators and std::copy:

    size_t height = 640, width = 512;
    size_t sliceSize = height * width;
    size_t sliceCount = FileBuffer.size() / sliceSize;

    size_t newHeight = height - 20, newWidth = width - 92;
    size_t newSliceSize = newHeight * newWidth;
    size_t newImageSize = newSliceSize * sliceCount;

    std::vector<data_type> NewFileBuffer(newImageSize);

    auto it_buffer = FileBuffer.begin();
    auto it_new_buffer = NewFileBuffer.begin();

    for (size_t i = 0; i < sliceCount; ++i)
    {
        // skip 20 first lines, remove and uncomment the next line
        // if you want to skip the last ones
        auto it_line = it_buffer + 20*width;
        //auto it_line = it_buffer;

        for (size_t j = 0; j < newHeight; ++j)
        {
            auto it_column = it_line + 46;

            it_new_buffer = std::copy(
                it_column,
                it_column + newWidth,
                it_new_buffer
            );

            it_line += width; // go to next line
        }

        it_buffer += sliceSize; // go to next slice
    }

Finally, write the file:

    std::basic_ofstream<data_type> file_out{
        "test2.vol",
        std::ofstream::binary
    };
    std::copy(
        NewFileBuffer.begin(),
        NewFileBuffer.end(),
        std::ostreambuf_iterator<data_type>(file_out)
    );
}