Bilinear Interpolation image resizing marks & 'splotches' - c

I'm making an image resizing program in C and at the moment I'm having trouble with the Bilinear Interpolation function (it's one of many I'm using). This problem only arises for 16-bit bitmaps, If I use 24-bit versions, it resizes them perfectly.
Here's my code for the Bilinear Interpolation. n_w and n_h are the new widths and heights of the image:
#define getelm(x) (((pix+index)->x)*(1-xdiff)*(1-ydiff))+(((pix+index+1)->x)*(xdiff)*(1-ydiff))+(((pix+index+o_w)->x)*(1-xdiff)*(ydiff))+((pix+index+o_w+1)->x)*xdiff*ydiff
int pad = (2*n_w) & 3;
if (pad)
pad = 4-pad;
uint16_t *buffer;
if (buf)
buffer = malloc(2*n_w);
for (i = 0; i < n_h; i++) {
for (j = 0; j < n_w; j++) {
x = (int)(j*xrat);
y = (int)(i*yrat);
xdiff = (xrat*j)-x;
ydiff = (yrat*i)-y;
index = y*o_w+x;
uint16_t container = 0;
container |= (int)(round(getelm(b))) << 11;
container |= (int)(round(getelm(g))) << 5;
container |= (int)round(getelm(r));
if (buf)
*(buffer+j) = container;
else
fwrite(&container, 1, 2, dest);
}
if (buf)
fwrite(buffer, 1, 2*n_w, dest);
fwrite(&pad, 1, pad, dest);
}
My 24-bit version of this code (where the only difference is that the container is not used and instead 3 8-bit integers hold the RGB values) works beautifully.
This code, however, gives weird results. Look at the image below:
When I resize this, it gives me this back:
I can't see why this would be happening, especially when it works for 24-bit bitmaps, and also that some other resizing algorithms (Nearest Neighbour for example) work with 16-bit in the same way that this should.
EDIT:
I don't think it's an overflow problem, because adding the following code gives no output when run:
if (MAX((int)(getelm(b)), 31) > 31)
printf("blue overflow: %.10f\n", (getelm(b)));
if (MAX((int)(getelm(g)), 63) > 63)
printf("green overflow: %.10f\n", (getelm(g)));
if (MAX((int)(getelm(r)), 31) > 31)
printf("red overflow: %.10f\n", (getelm(r)));
EDIT 2:
I don't think it's an underflow problem either, this does nothing:
if ((getelm(b)) < 0 || (getelm(g)) < 0 || (getelm(r)) < 0)
printf("Underflow\n");

Assuming the data in pix have the type
struct
{
uint16_t r : 5;
uint16_t g : 6;
uint16_t b : 5;
};
there's a bug in the calculation of container. Using round won't always prevent overflow. The next code will:
uint16_t container = 0;
container |= ((int)(round(getelm(b))) & 31) << 11; // optionally
container |= ((int)(round(getelm(g))) & 63) << 5;
container |= ((int)round(getelm(r)) & 31);
or to retain a maximum of the lost information:
uint16_t container = 0;
container |= min((int)(round(getelm(b))) , 31) << 11;
container |= min(((int)(round(getelm(g))) , 63) << 5;
container |= min(((int)round(getelm(r)) , 31);
EDIT
Since pix->r, pix->g and pix->b come from 8bit values, a same reasoning apply to them and their range needs to be checked.
Since a white region turns to purple this means that the green color is suppressed due to overflows or it's read as zero in the first place. In this case inspecting read color can help.
Similarly, a black color turns into green means a bit representing a small value is shifted and the color is somehow inverted.
To find the bug I recommend splitting the code into small functions and asserting the input of each one of them.

Related

Reading CAN message (PCAN-Router Pro FD)

please I have a problem with writing a code which will read a CAN message, edit it (limit to some maximum value) and then send back with same ID.
I´m using PCAN-Router Pro FD and will show you their example of such thing - basically same as mine but I have no idea what some of the numbers or operations are. [1]: https://i.stack.imgur.com/6ZDHn.jpg
My task is to: 1) Read CAN message with these parameters (ID = 0x120h, startbit 8, length 8 bit and factor 0,75)
2) Limit this value to 100 (because the message should have info about coolant temperature.)
3) If the value was below 100, dont change anything. If it was higher, change it to 100.
Thanks for any help !
Original code:
// catch ID 180h and limit a signal to a maximum
else if ( RxMsg.id == 0x180 && RxMsg.msgtype == CAN_MSGTYPE_STANDARD)
{
uint32_t speed;
// get the signal (intel format)
speed = ( RxMsg.data32[0] >> 12) & 0x1FFF;
// limit value
if ( speed > 6200)
{ speed = 6200;}
// replace the original value
RxMsg.data32[0] &= ~( 0x1FFF << 12);
RxMsg.data32[0] |= speed << 12;
}
After consulting the matter in person, we have found the answer.
The structure type of the RxMsg contains a union allowing the data to be accessed in 4-Byte chunks RxMsg.data32, 2-Byte chunks RxMsg.data16, or 1-Byte chunks RxMsg.data8. Since the temperature is located at the 8th bit and it is 1 Byte long, it can be accessed without using the binary masks, bit shifts and bitwise-logical-assignment operators at all.
// more if-else statements...
else if (RxMsg.id == 0x120 && RxMsg.msgtype == CAN_MSGTYPE_STANDARD)
{
uint8_t temperature = RxMsg.data8[1];
float factor = 0.75;
if (temperature * factor > 100.0)
{
temperature = (int)(100 / factor);
}
RxMsg.data8[1] = temperature;
}
The answer assumes that the startbit is the most significant bit in the message buffer and that the temperature value must be scaled down by the mentioned factor. Should the startbit mean the least significant bit, the [1] index could just be swapped out for [62], as the message buffer contains 64 Bytes in total.
The question author was not provided with a reference sheet for data format, so the answer is based purely on the information mentioned in the question. The temperature scaling factor is yet to be tested (will edit this after confirming it works).

Flipping Pebble Screen Issue

I'm writing a Pebble Time Watch app using Pebble SDK 3.0 on the basalt platform that requires text to be displayed upsidedown.
The logic is:-
Write to screen
Capture screen buffer
Flip screen buffer (using flipHV routine, see below)
Release buffer.
After a fair amount of experimentation I've got it working after a fashion but the (black) text has what seems to be random vertical white lines through it (see image below) which I suspect is something to do with shifting bits.
The subroutine I'm using is:-
void flipHV(GBitmap *bitMap) {
GRect fbb = gbitmap_get_bounds(bitMap);
int Width = 72; // fbb.size.w;
int Height = 84; // fbb.size.h;
uint32_t *pBase = (uint32_t *)gbitmap_get_data(bitMap);
uint32_t *pTopRemainingPixel = pBase;
uint32_t *pBottomRemainingPixel = pBase + (Height * Width);
while (pTopRemainingPixel < pBottomRemainingPixel) {
uint32_t TopPixel = *pTopRemainingPixel;
uint32_t BottomPixel = *pBottomRemainingPixel;
TopPixel = (TopPixel << 16) | (TopPixel >> 16);
*pBottomRemainingPixel = TopPixel;
BottomPixel = (BottomPixel << 16) | (BottomPixel >> 16);
*pTopRemainingPixel = BottomPixel;
pTopRemainingPixel++;
pBottomRemainingPixel--;
}
}
and its purpose is to work though the screen buffer taking the first pixel and swapping with the last one, the second one and swapping it with the second last one etc etc.
Because each 32 bit 'byte' holds 2 pixels I also need to rotate it through 16 bits.
I suspect that that is where the problem lies.
Can someone have a look at my code and see if they can see what is going wrong and put me right. I should say that I'm both a C and Pebble SDK newbie so please explain everything as if to a child!
Your assignments like
TopPixel = (TopPixel << 16) | (TopPixel >> 16)
swap pixels pair-wise
+--+--+ +--+--+
|ab|cd| => |cd|ab|
+--+--+ +--+--+
What you want instead is a full swap:
+--+--+ +--+--+
|ab|cd| => |dc|ba|
+--+--+ +--+--+
That can be done with even more bit-fiddling, e.g
TopPixel = ((TopPixel << 24) | // move d from 0..7 to 24..31
((TopPixel << 8) & 0x00ff0000) | // move c from 8..15 to 16..23
((TopPixel >> 8) & 0x0000ff00) | // move b from 16..23 to 8..15
((TopPixel >> 24) | // move a from 24..31 to 0..7
or - way more readable(!) - by using GColor8 instead of uint32_t and a loop on a per-pixel-basis:
// only loop to half of the distance to avoid swapping twice
for (int16_t y = 0; y <= max_y / 2; y++) {
for (int16_t x = 0; x <= max_x / 2; x++) {
GColor8 *value_1 = gbitmap_get_bytes_per_row(bmp) * y + x;
GColor8 *value_2 = gbitmap_get_bytes_per_row(bmp) * (max_y - y) + (max_x - x);
// swapping the two pixel values, could be simplified with a SWAP(a,b) macro
GColor8 tmp = *value_1;
*value_1 = *value_2;
*value_2 = tmp;
}
}
Disclaimer: I haven't compiled this code. It might also be necessary to cast the gbitmap_get_byes_per_row()... expressions to GColor8*. And the whole pointer arithmetic can be tuned if you see that this is a performance bottle-neck.
It turns out that I needed to replace all of the uint32_t with uint8_t and do away with the shifting.

Get the character dominant from a string

Okay.. according to the title i am trying to figure out a way - function that returns the character that dominates in a string. I might be able to figure it out.. but it seems something is wrong with my logic and i failed on this. IF someome can come up with this without problems i will be extremelly glad thank you.
I say "in a string" to make it more simplified. I am actually doing that from a buffered data containing a BMP image. Trying to output the base color (the dominant pixel).
What i have for now is that unfinished function i started:
RGB
bitfox_get_primecolor_direct
(char *FILE_NAME)
{
dword size = bmp_dgets(FILE_NAME, byte);
FILE* fp = fopen(convert(FILE_NAME), "r");
BYTE *PIX_ARRAY = malloc(size-54+1), *PIX_CUR = calloc(sizeof(RGB), sizeof(BYTE));
dword readed, i, l;
RGB color, prime_color;
fseek(fp, 54, SEEK_SET); readed = fread(PIX_ARRAY, 1, size-54, fp);
for(i = 54; i<size-54; i+=3)
{
color = bitfox_pixel_init(PIXEL_ARRAY[i], PIXEL_ARRAY[i+1], PIXEL_ARRAY[i+2);
memmove(PIX_CUR, color, sizeof(RGB));
for(l = 54; l<size-54; l+=3)
{
if (PIX_CUR[2] == PIXEL_ARRAY[l] && PIX_CUR[1] == PIXEL_ARRAY[l+1] &&
PIX_CUR[0] == PIXEL_ARRAY[l+2])
{
}
Note that RGB is a struct containing 3 bytes (R, G and B).
I know thats nothing but.. thats all i have for now.
Is there any way i can finish this?
If you want this done fast throw a stack of RAM at it (if available, of course). You can use a large direct-lookup table with the RGB trio to manufacture a sequence of 24bit indexes into a contiguous array of counters. In partial-pseudo, partial code, something like this:
// create a zero-filled 2^24 array of unsigned counters.
uint32_t *counts = calloc(256*256*256, sizeof(*counts));
uint32_t max_count = 0
// enumerate your buffer of RGB values, three bytes at a time:
unsigned char rgb[3];
while (getNextRGB(src, rgb)) // returns false when no more data.
{
uint32_t idx = (((uint32_t)rgb[0]) << 16) | (((uint32_t)rgb[1]) << 8) | (uint32_t)rgb[2];
if (++counts[idx] > max_count)
max_count = idx;
}
R = (max_count >> 16) & 0xFF;
G = (max_count >> 8) & 0xFF;
B = max_count & 0xFF;
// free when you have no more images to process. for each new
// image you can memset the buffer to zero and reset the max
// for a fresh start.
free(counts);
Thats it. If you can afford to throw a big hulk of memory at this a (it would be 64MB in this case, at 4 bytes per entry at 16.7M entries), then performing this becomes O(N). If you have a succession of images to process you can simply memset() the array back to zeros, clear max_count, and repeat for each additional file. Finally, don't forget to free your memory when finished.
Best of luck.

How to optimize C for loop for font rendering on oled display

I need to optimize this function: Any strange way to optimize the for loop? (early break i think can't be possible)
void SeeedGrayOLED::putChar(unsigned char C)
{
if(C < 32 || C > 127) //Ignore non-printable ASCII characters. This can be modified for multilingual font.
{
C=' '; //Space
}
uint8_t k,offset = 0;
char bit1,bit2,c = 0;
for(char i=0;i<16;i++)
{
for(char j=0;j<32;j+=2)
{
if(i>8){
k=i-8;
offset = 1;
}else{
k=i;
}
// Character is constructed two pixel at a time using vertical mode from the default 8x8 font
c=0x00;
bit1=(pgm_read_byte(&hallfetica_normal[C-32][j+offset]) >> (8-k)) & 0x01;
bit2=(pgm_read_byte(&hallfetica_normal[C-32][j+offset]) >> ((8-k)-1)) & 0x01;
// Each bit is changed to a nibble
c|=(bit1)?grayH:0x00;
c|=(bit2)?grayL:0x00;
sendData(c);
}
}
}
I've got a font in the array hallfetica_normal, is an array of array of uint8_t, that maybe compressed or something like that?
This code run on a arduino, ad i've to run a countdown from 500 to 0 with one unit down every 10/20ms.
EDIT
This is the new code after yours indication, thanks all:
I'm looking to organise the font differently to permit less call to pgm_read_byte.. (something like changing the orientation... i wonder)
void SeeedGrayOLED::putChar(unsigned char C)
{
if(C < 32 || C > 127) //Ignore non-printable ASCII characters. This can be modified for multilingual font.
{
C=' '; //Space
}
char c,byte = 0x00;
unsigned char nibble_lookup[] = { 0, grayL, grayH, grayH | grayL };
for(int ii=0;ii<2;ii++){
for(int i=0;i<8;i++)
{
for(int j=0;j<32;j+=2)
{
byte = pgm_read_byte(&hallfetica_normal[C-32][j+ii]);
c = nibble_lookup[(byte >> (8-i)) & 3];
sendData(c);
}
}
}
}
Well, you seem to be reading the same byte twice in a row unnecessarily via pgm_read_byte(&hallfetica_normal[C-32][j+offset]). You could load that once into a local variable.
Additionally, you could avoid the if(i>8){ check per iteration by breaking up the code into two loops; one where i goes from 0 to 8 and another where it goes from 9 to 15. (Although I suspect you really intended >= here, making the loop boundaries 0-7 then 8-15.) That also means things like offset become constant values, which will help.
In an effort to make the inner loop as fast as possible, I'd try to get rid of all branching with a lookup table and see whether that helped.
First, I'd define the lookup table outside the loop:
/* outside the loop */
unsigned char h_lookup[] = { 0, grayH };
unsigned char l_lookup[] = { 0, grayL };
Then inside the loop, since you're testing the least-significant bit, you can use that as an index into the lookup table. If it's clear, then the lookup index will be 0. If it's set, then the lookup index will be 1:
/* inside the loop */
byte = pgm_read_byte(&hallfetica_normal[C-32][j+offset]);
c = h_lookup[((byte >> (8-k)) & 0x01)] |
l_lookup[((byte >> (8-k-1)) & 0x01)]
sendData(c);
Since you're masking and testing 2 adjacent bits, 8-k and 8-k-1, you could list all 4 possibilities in a single lookup table:
/* Outside loop */
unsigned char nibble_lookup[] = { 0, grayL, grayH, grayH | grayL };
And then the lookup becomes dramatically simplified.
/* loop */
byte = pgm_read_byte(&hallfetica_normal[C-32][j+offset]);
c = nibble_lookup[(byte >> (8-k)) & 3];
sendData(c);
The other answer has addressed what to do about the branches in the top part of your inner loop.

ASCII compressor works for short test file, not on long

The current project in Systems Programming is to come up with an ASCII compressor that removes the top zero bit and writes the contents to the file.
In order to facilitate decompression, the original file size is written to file, then the compressed char bytes. There are two files to run tests on- one that is 63 bytes long, and the other is 5344213 bytes. My code below works as expected for the first test file, as it writes 56 bytes of compressed text plus 4 bytes of file header.
However, when I try it on the long test file, the compressed version is 3 bytes shorter than the original, when it should be roughly 749KiB smaller, or 14% of original size. I've worked out the binary bit shift values for the first two write loops of the long test file, and they match up what is being recorded on my test printout.
while ( (characters= read(openReadFile, unpacked, BUFFER)) >0 ){
unsigned char packed[7]; //compression storage
int i, j, k, writeCount, endLength, endLoop;
//loop through the buffer array
for (i=0; i< characters-1; i++){
j= i%7;
//fill up the compressed array
packed[j]= packer(unpacked[i], unpacked[i+1], j);
if (j == 6){
writeCalls++; //track how many calls made
writeCount= write(openWriteFile, packed, sizeof (packed));
int packedSize= writeCount;
for (k=0; k<7 && writeCalls < 10; k++)
printf("%X ", (int)packed[k]);
totalWrittenBytes+= packedSize;
printf(" %d\n", packedSize);
memset(&packed[0], 0, sizeof(packed)); //clear array
if (writeCount < 0)
printOpenErrors(writeCount);
}
//end of buffer array loop
endLength= characters-i;
if (endLength < 7){
for (endLoop=0; endLoop < endLength-1; endLoop++){
packed[endLoop]= packer(unpacked[endLoop], unpacked[endLoop+1], endLoop);
}
packed[endLength]= calcEndBits(endLength, unpacked[endLength]);
}
} //end buffer array loop
} //end file read loop
The packer function:
//calculates the compressed byte value for the array
char packer(char i, char j, int k){
char packStyle;
switch(k){
//shift bits based on mod value with 8
case 0:
packStyle= ((i & 0x7F) << 1) | ((j & 0x40) >> 6);
break;
case 1:
packStyle= ((i & 0x3F) << 2) | ((j & 0x60) >> 5);
break;
case 2:
packStyle= ((i & 0x1F) << 3) | ((j & 0x70) >> 4);
break;
case 3:
packStyle= ((i & 0x0F) << 4) | ((j & 0x78) >> 3);
break;
case 4:
packStyle= ((i & 0x07) << 5) | ((j & 0x7C) >> 2);
break;
case 5:
packStyle= ((i & 0x03) << 6) | ((j & 0x7E) >> 1);
break;
case 6:
packStyle= ( (i & 0x01 << 7) | (j & 0x7F));
break;
}
return packStyle;
}
I've verified that there are 7 bytes written out every time the packed buffer is flushed, and there are 763458 write calls made for the long file, which match up to 5344206 bytes written.
I'm getting the same hex codes from the printout that I worked out in binary beforehand, and I can see the top bit of every byte removed. So why aren't the bit shifts being reflected in the results?
Ok, since this is homework I'll just give you a few hints without giving out a solution.
First are you sure that the 56 bytes you get on the first file are the right bytes? Sure the count looks good, but you got lucky on count (proof is the second test file). I can immediately see at least two key mistakes in the code.
To make sure you have the right output, the byte count is not enough. You need to dig deeper. How about checking the bytes themselves one by one. 63 characters is not that much to go heh? There are many ways you can do this. You could use od (a pretty good Linux/Unix tool to look at the binary contents of files, if you're on Windows use some Hex editor). Or you could print out debug information from within your program.
Good luck.
Why do you expect the output to be 14% shorter than the input? How could it, when you store a byte into packed as many times as there are input bytes, except for the last group? The size of the output will always be within 7 of the size of the input.

Resources