How to write only regular intermediate elements from char buffer to disk in C ++

How can I write only every third element in the char buffer for quick download in C ++?

I get a three-channel image from my camera, but each channel contains the same information (grayscale image). I would like to record only one channel on a disc to save space and make recording faster, as it is part of a real-time data acquisition system.

The C ++ ofstream :: write command seems to write only continuous blocks of binary data, so my current code writes all three channels and is too slow:

char * data = getDataFromCamera(); int dataSize = imageWidth * imageHeight * imageChannels; std::ofstream output; output.open( fileName, std::ios::out | std::ios::binary ); output.write( data, dataSize ); 

I would like the last line to be replaced with the following call:

 int skipSize = imageChannels; output.write( data, dataSize, skipSize ); 

where skipSize will force writing to put only every third in the output file. However, I could not find any function that does this.

I would like to hear any ideas for quickly getting one channel recorded to disk. Thank.

+4
c ++ file-io
Jul 20 2018-10-18T00:
source share
6 answers

Let's say your buffer is 24-bit RGB, and you are using a 32-bit processor (so that operations with 32-bit objects are most efficient).

For more speed, let you work with a 12-byte chunk at a time. In twelve bytes we will have 4 pixels, for example:

 AAABBBCCCDDD 

What are 3 32-bit values:

 AAAB BBCC CDDD 

We want to turn this into an ABCD (single 32-bit value).

We can create an ABCD by applying a mask to each input and ORing.

 ABCD = A000 | 0BC0 | 000D 

In C ++, with a little-endian processor, I think it will be:

 unsigned int turn12grayBytesInto4ColorBytes( unsigned int buf[3] ) { return (buf[0]&0x000000FF) // mask seems reversed because of little-endianness | (buf[1]&0x00FFFF00) | (buf[2]&0xFF000000); } 

Probably the fastest way to do this is another conversion to a different buffer, and then dump the dump to disk, rather than go directly to disk.

+2
Jul 20 '10 at 15:36
source share

You probably have to copy every third element to the buffer, and then write this buffer to disk.

+6
Jul 20 '10 at 15:14
source share

You can use the codecvt face on local to filter out part of the output.
After creating, you can fill any stream using a suitable local one, and it will only see every third character in the input.

 #include <locale> #include <fstream> #include <iostream> class Filter: public std::codecvt<char,char,mbstate_t> { public: typedef std::codecvt<char,char,mbstate_t> MyType; typedef MyType::state_type state_type; typedef MyType::result result; // This indicates that we are converting the input. // Thus forcing a call to do_out() virtual bool do_always_noconv() const throw() {return false;} // Reads from -> from_end // Writes to -> to_end virtual result do_out(state_type &state, const char *from, const char *from_end, const char* &from_next, char *to, char *to_limit, char* &to_next) const { // Notice the increment of from for(;(from < from_end) && (to < to_limit);from += 3,to += 1) { (*to) = (*from); } from_next = from; to_next = to; return((to > to_limit)?partial:ok); } }; 

Once you have the facet, you need to know how to use it:

 int main(int argc,char* argv[]) { // construct a custom filter locale and add it to a local. const std::locale filterLocale(std::cout.getloc(), new Filter()); // Create a stream and imbue it with the locale std::ofstream saveFile; saveFile.imbue(filterLocale); // Now the stream is imbued we can open it. // NB If you open the file stream first. // Any attempt to imbue it with a local will silently fail. saveFile.open("Test"); saveFile << "123123123123123123123123123123123123123123123123123123"; std::vector<char> data[1000]; saveFile.write( &data[0], data.length() /* The filter implements the skipSize */ ); // With a tinay amount of extra work // You can make filter take a filter size // parameter. return(0); } 
+5
Jul 20 '10 at 17:03
source share

There is no such functionality in the afaik standard library. Jerry Coffin's solution will work best. I wrote a simple snippet that should do the trick:

 const char * data = getDataFromCamera(); const int channelNum = 0; const int channelSize = imageWidth * imageHeight; const int dataSize = channelSize * imageChannels; char * singleChannelData = new char[channelSize]; for(int i=0; i<channelSize ++i) singleChannelData[i] = data[i*imageChannels]; try { std::ofstream output; output.open( fileName, std::ios::out | std::ios::binary ); output.write( singleChannelData, channelSize ); } catch(const std::ios_base::failure& output_error) { delete [] channelSize; throw; } delete [] singleChannelData; 

EDIT: I added try..catch. Of course, you could use std :: vector for more convenient code, but it can be a little slower.

+1
Jul 20 2018-10-20T00:
source share

First, I would mention that for maximum write speed you should write buffers that are multiples of the size of the sector (for example, 64 KB or 256 KB)

To answer your question, you will have to copy every third element from the source data to another buffer, and then write it to the stream.

If I remember correctly, "Intel Performance Privileges" has functions for copying buffers that skip a certain number of elements. Using IPP is likely to have faster results than your own copy program.

0
Jul 20 '10 at 3:19
source share

I am tempted to say that you have to read your data in a structure and then overload the insert statement.

 ostream& operator<< (ostream& out, struct data * s) { out.write(s->first); } 
0
Jul 20 2018-10-20T00:
source share



All Articles