Faster way to load matrix into byte array? - arrays

I am overlaying a custom drawn view on top of a video frame and sending that overlay pixel information to my microcontroller and encoding it into a byte array. Most of the RGB values are 0 because they are apart of the video frame and not the custom view I am drawing.
I am encoding the 32-bit rgb value into 4 different bytes, for each pixel. However, the frame is very slow and lags because I am looping through the entire frame and converting each value to a byte array. I understand what's causing the issue, but wondering if there is a way to speed it up.
I would remove the 0's and only pass values that have a valid RGB value, but I need to keep the position.
The matrix size has 518400 elements.
for (int i = 0; i < A.size(); i++) {
byte *t = (byte *) &A.mem[i];
byte t1 = t[0];
byte t2 = t[1];
byte t3 = t[2];
byte t4 = t[3];
if (t1 != '\0' || t2 != '\0' || t3 != '\0' || t4 != '\0') {
writeSerialData(t, 4);
}
}

You could check the four bytes against zero in one go:
int iMax = A.size();
for (int i = 0; i < iMax; ) {
int32_t *t = (int32_t *) &A.mem[i++];
if (*t) {
writeSerialData(t, 4);
}
}

Do you control the receiving part of that writeSerialData()? Then you should invent some encoding that specifies a new offset of the following data.
Also, are you using 16 millions colors in your overlay? If you are only using a few colors, they invented palette for that. If you know the colors upfront, you can hard-code them on both ends. If not - send a small header with that palette info before the image. That can cut the size of your data by 4 times. If you only have single color, you can further reduce your data to single bits.
UPDATE
By "encoding" I mean some special format of your data, other than simple RGB value. For example, do you use the Alpha channel (I noticed you didn't call your colors RGBA)? If not - you can use that byte for length-encoding, stating that the following color is repeated that many times (up to 255). This will save you some bandwidth on your zeroes (just say how many are there), as well as on a line of the same color (you don't need to send every pixel).

Related

Iterating over byte stream - performance vs code style

I want to perform some conversions of pixels in bitmap which is given as byte stream.
Depending on bytes per pixel value the final pixels are different, but the code is almost the same. This is because the byte stream is presented as pointer like uint8_t*, uint16_t*, uint32_t* and iterating is done with bytestream[x].
Currently my code looks like:
if (bytes per pixel == 1) {
uint8_t *pixels = (uint8_t *) stream;
// a lot of code iterating over pixels pointer
} else if (bytes per pixel == 2) {
uint16_t *pixels = (uint16_t *) stream;
// exactly the same lot of code iterating over pixels pointer
} else if (bytes per pixel == 4) {
uint32_t *pixels = (uint32_t *) stream;
// exactly the same lot of code iterating over pixels pointer
}
Problem is the code style, I don't like that the code is repeated. I can't put that into the separate function, because function require pointer in specific type.
In C++ I believe I could use template, but this is C.
The alternative is casting to specific pointer type per each pixel, so the ifology with repeated code would be smaller, but I believe this could harm the performance.
Something like that:
uint8_t *pixels = (uint8_t *) stream;
// a lot of code iterating with `pixel = pixels + i * bytesPerPixel
//finally setting pixels
some loop {
if (bytes per pixel == 1) {
// one or two lines
} else if (bytes per pixel == 2) {
// one or two lines
} else if (bytes per pixel == 4) {
// one or two lines
}
}
This is a lot smaller, but as I said before this could limit the performance, since we are doing this check per every pixel.
Is making such checks per pixel is troublesome on current machines (example ~1GHz ARM cpu)?

can i compare 2 SDL_Surface (whether they are the same or not)

While making a game with SDL2 in c,i have to compare 2 SDL_Surface to check a win condition but i couldn't find a way to do so
It seems you're interested in comparing two SDL_Surfaces, so here is how you do it. There is probably a better way to solve your specific problem, but anyways:
From the SDL Wiki, SDL_Surface has members of interest format, w, h, pitch, pixels.
format represents the pixel encoding information
format->format is the specific enumeration constant specifying a given encoding
w represents the number of pixels in a row of pixels in the surface
h represents the number of rows of pixels in the surface
pitch represents the byte length of a row
pixels is an array with all the pixel data
If you want to compare two SDL_Surfaces, you need to compare the pixels against one another. But first we should check that the pixel encoding and the dimensions match:
int SDL_Surfaces_comparable(SDL_Surface *s1, SDL_Surface *s2) {
return (s1->format.format == s2->format.format && s1->w == s2->w && s1->h == s2->h);
}
If SDL_Surfaces_comparable evaluates to true, we can check if two surfaces are equal by comparing the pixels fields byte by byte.
int SDL_Surfaces_equal(SDL_Surface *s1, SDL_Surface *s2) {
if (!SDL_Surfaces_comparable(s1, s2) {
return 0;
}
// the # of bytes we want to check is bytes_per_pixel * pixels_per_row * rows
int len = s1->format->BytesPerPixel * s1->pitch * s1->h;
for (int i = 0; i < len; i++) {
// check if any two pixel bytes are unequal
if (*(uint8_t *)(s1->pixels + i) != *(uint8_t *)(s2->pixels + i))
break;
}
// return true if we finished our loop without finding non-matching data
return i == len;
}
This assumes that pixel data is serialized as bytes without any padding, or that the padding is zeroed. I couldn't find any SDLPixel structure, so I'm assuming this is the standard way to compare pixels. I did find this link, which seems to verify my approach.

Stuck implementing boundary checks on frame windows for mean filtering

I have a function that successfully reads rgb values from a ppm and a function that successfully writes to a ppm. What I am trying is a function called denoiseImage that changes rgb values from a ppm using mean filtering with a frame window size n by n where n is odd. My intent is to go through each pixel, using it as the center point for the window n by n that surrounds it. I then take the mean values for each color (r,g,b) and divide by the number of pixels in the window and assign those new values to the rgb of every pixel in the window. However, I am unable to implement a check for the cases where the frame does not fully fit into pixels (for example, the frame center point is the top right pixel, a window of 3x3 will go to non existent points.) When it does not fit fully, I intend to use the available pixels that fit and take the mean of those numbers instead. So far, my code will only work for cases where the frame fully fits. My function:
RGB *denoiseImage(int width, int height, const RGB *image, int n)
{
int firstPos, lastPos, i = 0, j = 0, k, numofPix;
int sumR=0,sumG=0,sumB=0;
numofPix = (width * height);
RGB *pixels = malloc(numofPix * sizeof(RGB));
if (n == 1) //Case where the window size is 1 and therefore the image does not get changed.
{
return pixels;
}
for (j=0;j < numofPix;j++)
{
firstPos = (j - width) - ((n - 1)/2);
lastPos = (j + width) + ((n - 1)/2);
//Need to check boundary cases to prevent segmentation fault
for (k=firstPos;k<=lastPos;k++) //Seg fault. Unable to shrink frame to compensate for cases where the frame does not fit.
{
sumR+=image[k].r;
sumG+=image[k].g;
sumB+=image[k].b;
i++;
if (i = n) //Used to skip elements not in frame
{
j += (width-n);
i = 0;
}
}
sumR = sumR/(n*n); //Calculating mean values
sumG = sumG/(n*n);
sumB = sumB/(n*n);
for (k=firstPos;k<=lastPos;k++) //Assigning the RGB values with the new mean values.
{
pixels[k].r=sumR;
pixels[k].g=sumG;
pixels[k].b=sumB;
printf("%d %d %d ",pixels[k].r, pixels[k].g, pixels[k].b);
}
}
return pixels;
}
int main()
{
RGB *RGBValues;
int width, height, max;
int j = 0,testemp=3; //test temp is a sample frame size
char *testfile = "test.ppm";
char *testfile2 = "makeme.ppm";
RGBValues = readPPM(testfile, &width, &height, &max); //Function reads values from a ppm file correctly
RGBValues = denoiseImage(width,height, RGBValues, testemp,testing);
writePPM(testfile2,width,height,max,RGBValues); //Function writes values to a ppm file correctly
}
How would I implement a way to check if the frame fits or not?
This is a great question and luckily known in the image processing community.
Edges are always treated differently when it comes to 2D filtering.
One way to look at it is to extend the space in 2D and to fill the edges with extrapolated values from the middle.
For example, you may look into the http://www.librow.com/articles/article-1 and search for a media filter.
I am sure that you will find solution soon, since you are going into right direction.

How do you read pixel data from a PPM (P3) image into a Matrix in C?

I would like a matrix with each row corresponding to a pixel and each column being an R G B value from a PPM P3 image.
I attempted to create a Matrix with [imageWidth * imageHeight] [3] as the size then populate that but it seems to be taking forever. I think I have missed something, can anyone explain where I went wrong or perhaps suggest a better way to do this?
int pixels [imageSize] [3];
while(fgets(line,70,fd) != NULL){
for ( column = 0; column < (imageSize); column++){
for ( row = 0; row < 1; row++){
sscanf(line, "%d %d %d", &r, &g, &b );
pixels [column] [row] = r;
pixels [column] [row + 1] = g;
pixels [column] [row + 2] = b;
}
}
}
Line refers to the line read in by the fgets function.
The problem is clear in your revised code: you try to read all pixels of the image from each raster line. This won't run forever unless the fgets() blocks indefinitely, but if the raster dimensions are large and the file is organized with many lines of pixels then it may take a very long time. In any case, it's wrong, for on each iteration of the outermost loop -- that is, for each line read -- it overwrites all previously assigned pixel values.
Furthermore, your innermost loop, though not actually wrong, is highly obfuscatory. It always performs exactly one iteration; its iteration variable row in fact indexes pixel components (as opposed to rows of the raster), and row anyway has the value 0 on every iteration. It would be better to just remove it.
Overall, you're making this much harder than it needs to be. The PPM P3 format is designed in a way that makes it amenable to input via much simpler code. Each sample in a valid PPM P3 file is guaranteed to have whitespace before and after. On the other hand, the position and width of the fields and the number of them per line is not fixed. Together, those characteristics makes this one of the comparatively rare cases in which fscanf() is actually a better choice than fgets() + sscanf(). The line length limit makes the latter pretty good too, but you don't need the extra complication that brings.
Given that you want to read imageSize pixels from the FILE designated by fd, with that FILE initially positioned at the first character of the first line of the raster, this will do the trick:
for (pixel = 0; pixel < imageSize; pixel++) {
int numScanned = fscanf(fd, "%d %d %d",
&pixels[pixel][0],
&pixels[pixel][1],
&pixels[pixel][2]);
if (numScanned < 3) {
// ... handle file format or I/O error ...
break;
}
}

Function to find the xy location of a pixel

Since my previous question was certainly not very clean. And respectively i couldn't implement an exact solution of my problem. I have been working on a function that returns the byte offset of a pixel located in X/Y coords. For that purpose i have that:
dword bmp_find_xy (dword xp, dword yp)
{
dword w = 50; // to clarify thats the real widh of the sample image i use
dword bpx = (3*8); // using 3*8 as a reminder.
dword offset = (2+sizeof(BMP)+sizeof(DIB)); // this is the offset 54.
dword pitch = w * 3; // determining the widh of pixels. Pitch variable
dword row = w * 3; // determining the widh of pixels. Row variable.
dword pixAddress; // result variable
if(pitch % 4 != 0) pitch += 4 - (pitch % 4); // finding the pitch (row+padding)
pixAddress = (offset) + pitch * yp + ((xp * bpx) / 8); // finding the address
return pixAddress;
}
So the question won't be like "What am i doing wrong/why im receiving weird errors". The question is.. am i doing it correct? On first tests it seems to work. But i am somehow unsure. Once it is confirmed thats the correct way.. I'll delete the question.
Your code looks like it gives the correct result to me. However it is inconsistent in itself.
In the row (yp) addressing, you assume that every pixel has 3 bytes.
In the column (xp) addressing, you assume that every pixel has 3*8 bits.
So why use bytes in the first case, bits in the second case? I think the code would be cleaner like this:
dword width = 50; // image width
dword channels = 3; // number of color channels
dword bpp = 8; // depth in bits
dword single = (channels*bpp)/8; // size of a pixel in bytes
dword offset = (2+sizeof(BMP)+sizeof(DIB)); // this is the offset 54.
dword rowsize = width*single; // size of a row in memory
if (rowsize % 4 != 0)
rowsize += 4 - (rowsize % 4); // account for padding
dword pixAddress; // result variable
pixAddress = offset + yp*rowsize + xp*single; // finding the address
return pixAddress;
Also, you can read the width, channel and bpp from the header.
Next, your code would be faster if you get the address of the first pixel in a row first, then keep it to iterate through the row (not recompute the whole thing every time). Here is an illustration of a typical task running over all pixels. Note that I do not use the same coding style as in the original question here.
unsigned char maxGreen = 0;
for (int y = 0; y < height; y++) {
unsigned char *row = bitmap.getRowPtr(y);
for (int x = 0; x < width; x++) {
unsigned char *pixel = row + bitmap.getColumnOffset(x);
if (pixel[2] > maxGreen)
maxGreen = pixel[2];
}
}
// maxGreen holds the maximum value in the green channel observed in the image
As you can see, in this example the offset, padding etc. calculations only need to be done once per row in the getRowPtr() function. Per pixel we only need to do the offset calculation (a simple multiplication) in the getColumnOffset() function.
This makes the example much faster, when breaking down how many calculations need to be done per pixel.
Last, I would never write code to read a BMP myself! Use a library for that!

Resources