How to convert input image to 3D image format - c

I am trying to write image rotation code in C.
int width, height, channels;
unsigned char *img = stbi_load("butterfly.jpg", &width, &height, &channels, 0);
I am reading the image as seen. I want to convert these image data to 3D (row, column, channel) array format to be able to do this line:
if (Xprime >= 1 && Yprime >= 1 && Xprime <= height && Yprime <= width)
{
pixel = img[Xprime][Yprime];
}
returnedOutImage[i][j] = pixel;
}

You could cast it to VLA.
I assume that pixels are ordered by y, next by x, finally by the channel.
unsigned char (*img3d)[width][channels] = (void*)img;
Now you can access it by img3[y][x][c].
There is a question if such a cast violates strict aliasing rules.

Related

Swscale - image patch (NV12) color conversion - invalid border

The goal is to convert NV12 to BGR24 image, more exactly an image patch (x:0, y:0, w:220, h:220).
The issue is the undefined pixel column on the right of the converted patch as shown:
The question is why is this happening (even though the coordinates and the dimensions of the patch have even values) ?
(Interestingly enough for an odd width value, that issue is not present)
The patch has the following bounding box: (x:0, y:0, w:220, h:220).
The behavior should be reproducible with any image. Conversion can be done using the ppm conversion page.
The following code creates a nv12 image from a bgr24 image and then converts a nv12 patch back to bgr24 patch. If everything worked properly the output should have been identical to a source image.
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
void readPPM(const char* filename, uint8_t** bgrData, int* stride, int* w, int* h)
{
FILE* fp = fopen(filename, "rb");
fscanf(fp, "%*s\n"); //skip format check
fscanf(fp, "%d %d\n", w, h);
fscanf(fp, "%*d\n"); //skip max value check
*stride = *w * 3;
*bgrData = av_malloc(*h * *stride);
for (int r = 0; r < *h; r++)
{
uint8_t* rowData = *bgrData + r * *stride;
for (int c = 0; c < *w; c++)
{
//rgb -> bgr
fread(&rowData[2], 1, 1, fp);
fread(&rowData[1], 1, 1, fp);
fread(&rowData[0], 1, 1, fp);
rowData += 3;
}
}
fclose(fp);
}
void writePPM(const char* filename, uint8_t* bgrData, int stride, int w, int h)
{
FILE* fp = fopen(filename, "wb");
fprintf(fp, "P6\n");
fprintf(fp, "%d %d\n", w, h);
fprintf(fp, "%d\n", 255);
for (int r = 0; r < h; r++)
{
uint8_t* rowData = bgrData + r * stride;
for (int c = 0; c < w; c++)
{
//bgr -> rgb
fwrite(&rowData[2], 1, 1, fp);
fwrite(&rowData[1], 1, 1, fp);
fwrite(&rowData[0], 1, 1, fp);
rowData += 3;
}
}
fclose(fp);
}
void bgrToNV12(uint8_t* srcData[4], int srcStride[4],
uint8_t* tgtData[4], int tgtStride[4],
int w, int h)
{
struct SwsContext* context = sws_getContext(w, h, AV_PIX_FMT_BGR24,
w, h, AV_PIX_FMT_NV12, SWS_POINT, NULL, NULL, NULL);
{
sws_scale(context,
srcData, srcStride, 0, h,
tgtData, tgtStride);
}
sws_freeContext(context);
}
void nv12ToBgr(uint8_t* srcData[4], int srcStride[4],
uint8_t* tgtData[4], int tgtStride[4],
int w, int h)
{
struct SwsContext* context = sws_getContext(w, h, AV_PIX_FMT_NV12,
w, h, AV_PIX_FMT_BGR24, SWS_POINT, NULL, NULL, NULL);
{
sws_scale(context,
srcData, srcStride, 0, h,
tgtData, tgtStride);
}
sws_freeContext(context);
}
int main()
{
//load BGR image
uint8_t* bgrData[4]; int bgrStride[4]; int bgrW, bgrH;
readPPM("sample.ppm", &bgrData[0], &bgrStride[0], &bgrW, &bgrH);
//create NV12 image from the BGR image
uint8_t* nv12Data[4]; int nv12Stride[4];
av_image_alloc(nv12Data, nv12Stride, bgrW, bgrH, AV_PIX_FMT_NV12, 16);
bgrToNV12(bgrData, bgrStride, nv12Data, nv12Stride, bgrW, bgrH);
//convert nv12 patch to bgr patch
nv12ToBgr(nv12Data, nv12Stride, bgrData, bgrStride, 220, 220); //invalid result (random column stripe)
//nv12ToBgr(nv12Data, nv12Stride, bgrData, bgrStride, 221, 220); //valid result
//save bgr image (should be exactly as original BGR image)
writePPM("sample-out.ppm", bgrData[0], bgrStride[0], bgrW, bgrH);
//cleanup
av_freep(bgrData);
av_freep(nv12Data);
return 0;
}
sws_scale makes a color conversion and scaling at the same time.
Most of the used algorithms need to include neighboring pixels in the calculation of a target pixel. Of course, this could lead to problems at the edges if the image dimensions are not a multiple of x. Where x depends on the used algorithms.
If you set the image dimensions here to a multiple of 8 (next multiple of 8 = 224), then it works without artifacts.
nv12ToBgr(nv12Data, nv12Stride, bgrData, bgrStride, 224, 224);
Demo
Using image dimensions 220 x 220 on the left, gives an artifact on the right edge of the converted patch.
If one chooses 224 x 224 it does not give an artifact, see the right image in the screenshot comparing both procedures.
Theoretically Required Minimum Alignment
Let's take a look at the YVU420 format:
The luma values are determined for each pixel. The color information, which is divided into Cb and Cr, is calculated from a 2x2 pixel block. The minimum image size would therefore be a 2 x 2 image block resulting in 6 bytes (i.e. 12 pixels per byte = 12 * 4 = 48bit = 6 bytes), see graphic here:
The minimum technical requirement is therefore an even width and height of the image.
You have defined the SWS_POINT flag for scaling, i.e. the nearest neighbor method is used. So theoretically for each output pixel the nearest input pixel is determined and used, which does not cause any alignment restrictions.
Performance
But an important aspect of the actual implementations of algorithms, however, is often performance. In this case, e.g. several adjacent pixels could be processed at once. Also do not forget the possibility of hardware-accelerated operations.
Alternative solution
If for some reason you need to stick to a 220x220 format, you can alternatively use the SWS_BITEXACT flag.
It does:
Enable bitexact output.
see https://ffmpeg.org/ffmpeg-scaler.html#scaler_005foptions
So in nv12ToBgr you would use something like:
struct SwsContext* context = sws_getContext(w, h, AV_PIX_FMT_NV12,
w, h, AV_PIX_FMT_BGR24, SWS_POINT | SWS_BITEXACT, NULL, NULL, NULL);
This doesn't give any artifacts either. If you have to convert a lot of frames, I would take a look at the performance.

How to use memset to add colours to a pixel array?

I have a program in C and SDL2 with a pixel array:
Uint32 *pixels = NULL;
pixels = (Uint32 *) malloc (sizeof(Uint32)*(Uint32)windowWidth*(Uint32)windowHeight);
memset(pixels, 255, (Uint32)windowWidth*(Uint32)windowHeight*sizeof(Uint32));
With memset, making my second parameter '255' sets all the pixels to white. Every other number below that sets the pixels to a shade of grey. How do I set these pixels to an actual colour?
I use this pixel array to set screen colour via the texture amongst other things. The texture has a pixel format of ARGB8888 so a four colour channel Hex value should work, no?
SDL_UpdateTexture(texture, NULL, pixels, (int)((Uint32)windowWidth * sizeof(Uint32)));
I have tried using hex such as 0xFFCC00FF but that just sets it to white.
The problem with memset is that it copies the same value to each byte. You want to use memcpy in a loop with a 4-byte value.
Uint32 numPixels = (Uint32)windowWidth * (Uint32)windowHeight;
Uint32 color = (Uint32)0xFFCC00FF;
for (Uint32 i = 0; i < numPixels; ++i)
{
memcpy(pixels + i * sizeof(Uint32), &color, sizeof(color));
}
Use a for loop:
Uint32 *pixels = NULL;
size_t nPixels = windowWidth * windowHeight;
pixels = malloc (sizeof (Uint32) * nPixels);
for (size_t c = 0; c < nPixels; c++) {
pixels[c] = color;
}
This doesn't use memset but thanks to #Banex I was able to come up with something that worked.
Uint32 colour = (Uint32)0xFFCC00FF;
int i,j;
for(i=0;i<windowWidth;i++)
{
for(j=0;j<windowHeight;j++)
{
pixels[i+j*windowWidth]=colour;
}
}

reading a UPC barcode from an image

i need some guidance on how to get a 12 digit barcode from a bmp file, i'm completely clueless on how approach this.
i started by reading the image into a bitmam, how can i continue?
example: the barcode of the image below is 081034489030.
how to i get these numbers?
void part1() {
int width, height;
unsigned char ** img = NULL;
img = readBMP("package.bmp", &height, &width);
}
unsigned char** readBMP(char* filename, int* height_r, int* width_r)
{
int i, j;
FILE* f;
fopen_s(&f,filename, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width
//from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int pad_needed = 4 - (3 * width) % 4; // pad calculation
int paddedRow = 3 * width + ((pad_needed != 4) ? pad_needed : 0);
unsigned char** map2d = (unsigned char**)malloc(width * sizeof(unsigned
char*)); // alocate memory for img 2d array
for (i = 0; i < width; i++) {
map2d[i] = (unsigned char*)malloc(height * sizeof(unsigned char));
}
unsigned char* data = (unsigned char*)malloc(paddedRow * sizeof(unsigned
char)); // allocate memory for each read from file
for (i = 0; i < height; i++) {
fread(data, sizeof(unsigned char), paddedRow, f); //read line from file
for (j = 0; j < width; j++) {
map2d[j][i] = (int)data[3 * j]; // insert data to map2d. jump 3,
//becasue we need only one value of the colors (RGB)
}
}
free(data);
fclose(f);
*width_r = width;
*height_r = height;
return map2d;
}
You need to apply computer vision techniques to:
Segment the barcode from the image
Decode the barcode information so that it can be further used in an application.
There is no single answer to this problem, and it will definitely not be a one-liner.
A way to start is by using a dedicated computer vision library like OpenCV. It will not only handle the image loading on your behalf, but enable you to apply advanced image processing algorithms on the loaded data. It supports C, Python, C#, so you should easily find the version that matches your language of choice.
Once OpenCV is added to your project, it is time to solve point number 1. A good algorithm to start from is described Detecting Barcodes in Images with Python and OpenCV. Don't get distracted by the use of Python, the same OpenCV functions are available in C as well, the idea is to understand the algorithm.
Assuming you now have a working segmentation algorithm, the last step is to decode the barcode itself. Here I would suggest Parts 2 and 3 of this article as a starting point. There are also pre-built libraries (if you Google, there are plenty of UPC decoders written in Java or C# like this one), so with a bit of digging you may be able to find an out-of-the-box solution.
Hope this helps.

Are my solutions to printing an error due to file size and storing values right?

PART 1: What I need to do is print out an error if the file size exceeds the 500 by 500 measurements (defined at the top as max_width and height). I
PART 2: The other part is that I have to read the pixel information from the input file and store it into a 2d array. Each pixel has 3 values for red, green, and blue, but I'm not sure if this matters.
My attempt at the solution:
PART 1:
void check_file_size //I'm not sure what to put as arguments since width/height are global
{
if (width > 500 && height > 500)
{
perror("Error: File size too big.\n");
}
}
PART 2:
#define max_width 500
#define max_height 500
int width, height
void read_header(FILE *new)
{
int max_color;
char P[10];
fgets(P, 10, new);
fscanf(new, "%d %d", &width, &height);
fscanf(new, "%d", &max_color);
}
void store_into_array(FILE *input)
{
int array[max_width][max_height];
for (x = 0; x < width; x++)
{
for (y = height; y >=0; y--)
{
fscanf(input, "%d", &array[x][y]);
}
}
}
Part 1
Function should take void arguments - this means none.
You want an or. Error if width OR height are too big.
Minor style note, you should be using the #defines here and they should be all uppercase.
void check_file_size(void) {
if (width > MAX_WIDTH || height > MAX_HEIGHT) {
perror("Error: File size too big.\n");
}
}
Part 2
You can loop through an array here like you are, but it's actually much nicer to cheat.
A C array of arrays or a straight array is the same thing with slightly different syntactic sugar.
Read the whole file into an array, see Reading the whole text file into a char array in C for implementation hints.
Cast the buffer into the final structure that you want.
// Make struct rgb match your data, details not supplied in the question
struct rgb {
uint8_t red;
uint8_t green;
uint8_t blue;
}
// Get width & height info as before
uint32_t buffer_size;
void* buffer;
load_file('filename', buffer, &buffer_size);
// Should verify that buffer_size == width * height * 3
struct rgb (*image_data)[width] = (struct rgb(*)[width])buffer;
// Note the above variable length array is a C99 feature
// Pre-C99 the same trick is a touch more ick
// Data can now be accessed as image_data[x][y].red; etc.
Sorry about the stdint.h variables, a habit I can't (and don't want to) break.

Take screenshot with openGL and save it as png

I'm trying to take a screenshot of full screen and save it as a png. I found a code here and modified it a bit. For the screenshot I use openGL and Glut and for the saving in png the gd library for c. All I'm getting is a black png and I can't figure out why. I searched in stackoverflow and found some posts, but unfortunately they didn't help. One of them was to use glReadBuffer( GL_FRONT); instead of glReadBuffer(GL_BACK); I tryed with both of them with no success. Here is my code:
int SVimage2file(char *filename){
int width = glutGet(GLUT_SCREEN_WIDTH);
int height = glutGet( GLUT_SCREEN_HEIGHT);
FILE *png;
GLubyte *OpenGLimage, *p;
gdImagePtr image;
unsigned int r, g, b;
int i,j,rgb;
png = fopen(filename, "wb");
if (png == NULL) {
printf("*** warning: unable to write to %s\n",filename);
return 1;
}
OpenGLimage = (GLubyte *) malloc(width * height * sizeof(GLubyte) * 3);
if(OpenGLimage == NULL){
printf("error allocating image:%s\n",filename);
exit(1);
}
printf("Saving to: %s .\n",filename);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadBuffer( GL_FRONT);
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, OpenGLimage);
p = OpenGLimage;
image = gdImageCreateTrueColor(width,height);
for (i = height-1 ; i>=0; i--) {
for(j=0;j<width;j++){
r=*p++; g=*p++; b=*p++;
rgb = (r<<16)|(g<<8)|b;
//printf("the rgb color %d\n", rgb );
gdImageSetPixel(image,j,i,rgb);
}
}
gdImagePng(image,png);
fclose(png);
gdImageDestroy(image);
}
What is it that I'm missing?
You could use the devil image library and take a screeshot with:
void takeScreenshot(const char* screenshotFile)
{
ILuint imageID = ilGenImage();
ilBindImage(imageID);
ilutGLScreen();
ilEnable(IL_FILE_OVERWRITE);
ilSaveImage(screenshotFile);
ilDeleteImage(imageID);
printf("Screenshot saved to: %s\n", screenshotFile);
}
takeScreenshot("screenshot.png");
If you don't reject to use C++ library, you should try PNGwriter! It write the picture pixel by pixel and their RGB values. Since the PNGwriter start form left-up corner while the glReadPixels() start from left-bottom, your code while like:
GLfloat* OpenGLimage = new GLfloat[nPixels];
glReadPixels(0.0, 0.0, width, height,GL_RGB, GL_FLOAT, OpenGLimage);
pngwriter PNG(width, height, 1.0, fileName);
size_t x = 1; // start the top and leftmost point of the window
size_t y = 1;
double R, G, B;
for(size_t i=0; i<npixels; i++)
{
switch(i%3) //the OpenGLimage array look like [R1, G1, B1, R2, G2, B2,...]
{
case 2:
B = (double) pixels[i]; break;
case 1:
G = (double) pixels[i]; break;
case 0:
R = (double) pixels[i];
PNG.plot(x, y, R, G, B);
if( x == width )
{
x=1;
y++;
}
else
{ x++; }
break;
}
}
PNG.close();
PS. I had also try libgd, but it seems only convert one image file (in the hard disk or in memory) to another format of image. But I think it still useful while you want to convert many PNG file to GIF format to create a GIF animate.

Resources