binary copy data from struct to std::vector - c++

I am using a lib which can load BMP images from memory.
I have a class which represents a BMP.
To load from memory I have to supply a pointer to some BMP formatted data in memory and a variable for the size of that data. (void* data, size_t length)
I want to store my data in a std::vector. (Avoids manual memory management)
I've attempted to write a function to return a std::vector<unsigned char>, but I don't think what I've got is very good.
std::vector<unsigned char> BMP::BITMAP::SaveMem() const
{
// memory storage
std::vector<unsigned char> memory;
BITMAPFILEHEADER f_head;
f_head.bfType = ushort_rev(((WORD)'B' << 0x08) | ((WORD)'M' << 0x00));
f_head.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + m_width_memory * m_height;
f_head.bfReserved1 = 0;
f_head.bfReserved2 = 0;
f_head.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
// build standard bitmap file header
BITMAPINFOHEADER i_head;
i_head.biSize = sizeof(BITMAPINFOHEADER);
i_head.biWidth = m_width;
i_head.biHeight = m_height;
i_head.biPlanes = 1;
i_head.biBitCount = m_bit_count;
i_head.biCompression = 0;
i_head.biSizeImage = m_width_memory * m_height;
i_head.biXPelsPerMeter = 0;
i_head.biYPelsPerMeter = 0;
i_head.biClrUsed = 0;
i_head.biClrImportant = 0;
// alloc
memory.resize(f_head.bfSize);
std::copy(&f_head, &f_head + sizeof(f_head), memory.at(0));
std::copy(&i_head, &i_head + sizeof(i_head), memory.at(0) + sizeof(f_head));
// write data
for(unsigned int y = 0; y < m_height; ++ y)
{
std::copy(&m_data[y * m_width_memory], m_data[y * m_width_memory + 3 * m_size_x], memory.at(0) + sizeof(f_head) + sizeof(i_head));
}
}
Clearly this doesn't compile. I can't think of any alternative to std::copy. Is this really the right tool for the job?
To make it compile I think I should change memory.at(x) to memory.data() + x... By doing this I would be using raw pointers - which is why I don't think std::copy is any better than memcpy.
Could I have some advice on this? It's somewhat an illogical task and had I known about this requirement earlier I would have stored my pixel data in an unsigned char with the bitmap file headers preceeding the data. Unfortunatly it will be a lot of work to change the design now, so I'd rather not touch it.

Three problems:
You want to copy bytes, but the std::copy function is provided a pointer to a BITMAPFILEHEADER (or BITMAPINFOHEADER) structure. You need to convert the pointers to bytes, like reinterpret_cast<uint8_t*>(&f_head).
The previous leads to other problems with the end of the data, the expression &f_head + sizeof(f_head) which really is equal to (&f_head)[sizeof(f_head)], and is way beyond the end of the structure. You need to use bytes here as well, as in reinterpret_cast<uint8_t*>(&f_head) + sizeof f_head.
The last problem is the destination for the std::copy call, as it needs to be a similar type as the source, i.e. a pointer to uint8_t (in the case of my casts). You can easily get that by doing e.g. &memory[0]. And for the second call &memory[sizeof f_head].

Related

casting a pointer to an array to a structure in efficient C++11 way

I have a big amount point cloud data that I read from a file into
char * memblock = new char [size];
where size is the size of data. Then I cast my data to float numbers
float * file_content = reinterpret_cast<float *>(memblock);
Now I would like to change the data from a pointer to an array and place it in a certain structure like std::vector<PointXYZ>.
vector.clear();
for (int i = 2; i < file_content_size; i+=3) {
vector.push_back(
PointXYZ(file_content[i-2], file_content[i-1], file_content[i] )
);
}
But I feel there must be a better way than just looping through the whole data, considering that the size of the vector is more than 1e6.
std::vector has a range constructor that you can use to copy the elements to the vector.
std::vector<PointXYZ> vec(memblock, memblock + size);
I believe this will be faster because you are not reallocating memory for every push_back, however you will still be doing a copy of all elements in memblock.
I think you alignment problems when you cast your char* raw data into a float*.
Generally you should arrange things so you cast other types to a char* because that is allowed to alias everything else and ensures you get correct alignment.
// create your array in the target type (float)
std::vector<float> file_content(size/sizeof(float));
// read the data in (cast to char* here)
file.read(reinterpret_cast<char*>(file_content.data()), size);
I honestly don't think you can get away from copying all the data.
std::vector<PointXYZ> points;
points.reserve(file_content.size() / 3);
for(auto i = 0ULL; i < file_content.size(); i += 3)
points.emplace_back(points[i], points[i + 1], points[i + 2])

Flatten array of structs efficiently

I'm looking for the most efficient way to flatten an array of structs in C++ for passing the flattend 1D array data as input to a cv::Mat. The struct looks as follows:
struct Color3
{
uint8_t red, green, blue;
}
My code then looks like this:
// Update color frame
cv::Mat colorMat = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3)
const Color3* colorPtr = colorFrame->getData(); // Get Frame from Library
std::vector<uchar> vecColorData;
data.reserve(1920 * 1080 * 3);
for (int i = 0; i < 1920 * 1080; ++i)
{
auto color = *colorPtr;
vecColorData.push_back(color.red);
vecColorData.push_back(color.green);
vecColorData.push_back(color.blue);
vecColorData++;
}
colorMat.data = vecColorData.data();
Is there a more efficient way than creating an intermediate std::vector and looping over the entire array? I guess I'm looking for something like:
colorMat.data = colorFrame->getData()
However, I'm getting the following error: a value of type Color3* cannot be assigned to an entity of type uchar*.
you don't need an intermediate vector.
If I understood, you want to assign the same RGB triple to all data.
It is also unclear to me if you have to allocate colorMat.data on your own or not.
If this is the case, once colorMat.data is allocated and sized 1920 * 1080 * 3, you can do something like the following:
uchar * data = colorMat.data;
for (int i = 0; i < 1920 * 1080; ++i)
{
*data++ = (uchar)colorPtr->red;
*data++ = (uchar)colorPtr->green;
*data++ = (uchar)colorPtr->.blue;
}
The following answer is not technically portable but will work on the vast majority of platforms you will encounter in real life.
It is extremely likely that your Color3 struct has no padding. You can veryify this by using a static_assert:
static_assert(sizeof(Color3) == sizeof(uint8_t) * 3);
With this confirmed you can cast an array of Color3 to an array of uint8_t and pass it directly to colorMat.data (assuming that member actually accepts uint8_t).
Your code therefore becomes:
cv::Mat colorMat = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3)
const Color3* colorPtr = colorFrame->getData(); // Get Frame from Library
colorMat.data = reinterpret_cast<const uint8_t*>(colorPtr);
Bear in mind I have never used the cv library and know nothing about the ownership requirements of the data pointer. The above just replicates what you're doing without the unnecessary std::vector.

Why is cv::Mat::data always pointing to a uchar?

I try to read a NEF file using LibRaw and then put it in a cv::Mat. The NEF file stores data as 12bit, this means I need 16 bit, so I ought to use CV_16UC4 like this:
Mat img1(height, width, CV_16UC4);
Libraw stores data as ushort*[4], so I thought that this should work:
for (i = 0; i < iwidth*height; i++) {
img1.data[4*i+1] = Processor.imgdata.image[i][0];
img1.data[4*i+2] = Processor.imgdata.image[i][1];
img1.data[4*i+3] = Processor.imgdata.image[i][2];
img1.data[4*i+4] = Processor.imgdata.image[i][3];
}
I also get a build error that data may be lost since a ushort to uchar conversion is going to take place, which makes sense, but still, how do I put data bigger than uchar in the data?
If you need pointer to raw data of specific type, using cv::Mat::ptr() is the best practice:
ushort* ptr = img1.ptr<ushort>();
for (i = 0; i < iwidth*height; i++) {
ptr[4*i+1] = Processor.imgdata.image[i][0];
ptr[4*i+2] = Processor.imgdata.image[i][1];
ptr[4*i+3] = Processor.imgdata.image[i][2];
ptr[4*i+4] = Processor.imgdata.image[i][3];
}
Please see documentation.
cv::Mat::data uses uchar in order avoid being a template class. In order to fill it with other image data you'll need to cast the data pointer. In your case try something like this:
Mat img1(height, width, CV_16UC4);
ushort * data = reinterpret_cast< ushort* >( img1.data );
for (i = 0; i < iwidth*height; i++) {
...
}
Alternatively, instead of changing the data pointer img1.data directly in your for-loop, you could consider using
the templated pixel access function cv::Mat::at<T>()
img1.at<Vec4w>(y,x) = reinterpret_cast<Vec4w>(Processor.imgdata.image[i])
use the specialized class Mat4w img(height, width) and then operator(y,x)
img1(y,x) = reinterpret_cast<Vec4w>(Processor.imgdata.image[i])
Mat.data looks like a uchar, but actually it contains all the ushort data in the memory. You can simply copy the memory to your ushort array, like this:
memcpy(your_array, img.data, your_array_size);

Memory error while using memcpy?

I'm using dcmtk library to modify the pixel data of a multi frame compressed dicom image. So, to do that, at one stage in an for loop I take the pixel data of each decompressed frame and modify them according my wish and try to concatenate each modify pixel data in a big memory buffer frame by frame. This core process of for loop is as below.
The problem is after the first iteration it gives memory at the line of the code where I call the function getUncompressedFrame. I think it's happening because of the line memcpy(fullBuffer+(i*sizeF),newBuffer,sizeF);, as when I remove that line there's no error at that time and the whole for loop works absolutely fine.
Could you please say me if I'm making a mistake in working with memcpy? Thanks.
Uint32 sizeF=828072;// I just wrote it to show what is the data type.
Uint8 * fullBuffer = new Uint8(int(sizeF*numOfFrames));//The big memory buffer
for(int i=0;i<numOfFrames;i++)
{
Uint8 * buffer = new Uint8[int(sizeF)];//Buffer for each frame
Uint8 * newBuffer = new Uint8[int(sizeF)];//Buffer in which the modified frame data is stored
DcmFileCache * cache=NULL;
OFCondition cond=element->getUncompressedFrame(dataset,i,startFragment,buffer,sizeF,decompressedColorModel,cache);
//I get the uncompressed individual frame pixel data
if(buffer != NULL)
{
for(unsigned long y = 0; y < rows; y++)
{
for(unsigned long x = 0; x < cols; x++)
{
if(planarConfiguration==0)
{
if(x>xmin && x<xmax && y>ymin && y<ymax)
{
index=(x + y + y*(cols-1))*samplePerPixel;
if(index<sizeF-2)
{
newBuffer[index] = 0;
newBuffer[index + 1] = 0;
newBuffer[index +2] = 0;
}
}
else
{
index=(x + y + y*(cols-1))*samplePerPixel;
if(index<sizeF-2)
{
newBuffer[index] = buffer[index];
newBuffer[index + 1] = buffer[index + 1];
newBuffer[index + 2] = buffer[index + 2];
}
}
}
}
}
memcpy(fullBuffer+(i*sizeF),newBuffer,sizeF);
//concatenate the modified frame by frame pixel data
}
Change the declaration of fullBuffer to this:
Uint8 * fullBuffer = new Uint8[int(sizeF*numOfFrames)];
Your code didn't allocate an array, it allocated a single Uint8 with the value int(sizeF*numOfFrames).
Uint8 * fullBuffer = new Uint8(int(sizeF*numOfFrames));
This allocates a single byte, giving it an initial value of sizeF*numOfFrames (after truncating it first to int and then to Uint8). You want an array, and you don't want to truncate the size to int:
Uint8 * fullBuffer = new Uint8[sizeF*numOfFrames];
^ ^
or, to fix the likely memory leaks in your code:
std::vector<Uint8> fullBuffer(sizeF*numOfFrames);
If the method getUncompressedFrame is doing an inner memcpy to cache, then it makes sense why, as you are passing a null pointer as argument for the cache, with no memory allocated.

How to send vector<vector<type>> via MapViewOfFile

I have the following code in a parent process:
vector<vector<double> > matrix(n); /* matrix NxM */
/* pushing data */
HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
0, PAGE_READWRITE, 0, 2*sizeof(int) + sizeof(double)*n*m, lpName);
LPVOID lp = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
mat_tmp* tmp = (mat_tmp*)lp;
tmp->n = n; tmp->m = m;
tmp->vv = vector<vector<double> >(matrix.begin(), matrix.end());
In a child process I'm trying to receive this vector >, but my child process terminate every time.
LPTSTR lpName = _tcsdup(TEXT(map_name));
HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE,
0, PAGE_READWRITE, 0, 2*sizeof(int) + sizeof(double)*n*m, lpName);
LPVOID lp = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
mat_tmp * tmp = (mat_tmp*)lp;
vector<vector<double> > matrix(tmp->vv.begin(), tmp->vv.end());
Error occurs when I'm trying to use data from matrix in child process.
And the little struct is following:
struct mat_tmp
{
int n; int m; vector<vector<double> > vv;
};
How can I receive my vector correctly in a child?
Its not going to work the way you are doing it at the moment. The mapped memory will contain n & m and the internal structure of the vector class which will probably be a couple of pointers, depending on your STL implementation. Followed by a load of junk. You'll not be able to safely access these pointers from the other process as it is in a different address space.
You need to actually copy the memory pointed to by the vector into the mapped memory and reconstruct the vector on the other side.
Additionally the size of the memory you need is not going to be sizeof(int)*n*m. It looks like you want to have 2 ints n & m in the structure and an collection of n*m doubles.
So, allocate 2*sizeof(int) + n*m*sizeof(double). Copy n & m into the memory and then copy the rows of the matrix in.
On the receiving side, read n & m and then deserialize the data into your vector of vectors.
Maybe make the struct like this:
struct mat_tmp
{
int n; int m; double vv[1];
};
Then, copy directly into the shared memory:
double* dst = tmp.vv;
for (size_t y = 0; y < matrix.size(); ++y) {
memcpy(dst, &(matrix[y][0]), matrix[y].size()*sizeof(double));
dst += matrix[y].size();
}
On the other side,
dst_matrix.resize(n);
for (size_t y = 0; y < n; ++y) {
dst_matrix[y].resize(m);
memcpy(&(dst_matrix[y][0]), tmp + (y * m), m * sizeof(double));
}
All untested and could be made more optimal but hopefully it explains better what you need to do.
You need to override the default allocator object for the vector to so that it works entirely in the memory space of the MapView, since the default allocator object will use heap memory for all allocations. There is a very old Dr.Dobbs article that Bjarne Stroustrup (creator of C++) wrote that tells exactly how to accomplish this task:
http://www.drdobbs.com/creating-stl-containers-in-shared-memory/184401639
This won't work, the vector will only hold a pointer, the actual data is heap-allocated. Your memcpy only copies the pointer, which is invalid on the side of the recipient.