Scaling down a BMP image in C - c

int main(void) {
setvbuf( stdout, NULL, _IONBF, 0 );
//Input File
FILE* infile;
infile = fopen("test.bmp", "rb");
//Vars for image
char bm[2];
int imageSize;
int fileSize;
int width, height;
char restOfDataOne[12];
char restOfDataTwo[28];
//Read Header Info
fread(bm, 1, 2, infile);
fread(&fileSize, 1, 4, infile);
imageSize = fileSize - 54;
fread(restOfDataOne, 1, 12, infile);
fread(&width, sizeof(int), 1, infile);
fread(&height, sizeof(int), 1, infile);
fread(restOfDataTwo, 1, 28, infile);
int rowWidth = width * 3;
//Read Image Data
unsigned char image[height][(width * 3)];
fread(image, sizeof(char), imageSize, infile);
//Close
fclose(infile);
//#################################################
//Small BMP
//#################################################
FILE* smallOut;
smallOut = fopen("small.bmp", "wb");
//Small Vars
int imageSizeSmall = imageSize / 4;
int fileSizeSmall = imageSizeSmall + 54;
int widthSmall = width / 2;
int heightSmall = height / 2;
int smallRowWidth = widthSmall * 3;
unsigned char imageSmall[heightSmall][smallRowWidth];
//Image Data
int c, d, e;
//For every 4 pixels.. store one in small image
for(c = 0; c < rowWidth; c++) {
for(d = 0; d < height; d++) {
//imageSmall[d/2][c/2] = image[d][c];
for(e = 0; e < 3; e++) {
//grab every 1 out of 4 and place into small?
}
}
}
So I have the following code which reads the bmp image, then I need to scale it down and output it to the smaller version which is half the width and half the height, therefore being 4 times smaller in all. So I know I have to grab 1 out of every 3 pixels? and put it into my new smallImage, but I have tried multiple things with nested for loops and just can't get the algorithm down. I have viewed multiple posts here on stackexchange but people are using libraries, which I cannot use. (Homework). I'm not looking for someone to do it for me, just looking for someone to point me in the right direction or give me a hint?
Full code here.
https://pastebin.com/ZVmXtmCx

You have a nice and detailed study for your problem at the following address:
http://www.davdata.nl/math/bmresize.html
I quote:
This is done by scanning these pixels, left to right, top to bottom
while projecting the destination pixel over the source bitmap.

Related

How to get pixel value of a grayscale image with 16 bits depth using libpng library?

I have a grayscale image with 16 bits depth. I want get pixel value of the images using libpng library. But, output of my program is all pixel values of that image between 0 to 255 at all. I checked it with ImageJ software with that image and the fact that they are between 0 to 65535. i don't know where i went wrong. This is my code.
#include <stdio.h>
#include <png.h>
int main(void) {
FILE *fp = fopen("sample.png", "r");
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop png_info = png_create_info_struct(png_ptr);
png_init_io(png_ptr, fp);
png_bytepp rows;
png_read_png(png_ptr, png_info, PNG_TRANSFORM_IDENTITY, NULL);
int WIDTH = png_get_image_width(png_ptr, png_info);
int HEIGHT = png_get_image_height(png_ptr, png_info);
rows = png_get_rows(png_ptr, png_info);
for (int h = 0; h < HEIGHT; h++) {
for (int w = 0; w < WIDTH; w++) {
printf("%d ", rows[h][w]);
}
printf("\n");
}
return 0;
}
Please help me. Thank you very much.
It works if you change the types on the following lines:
unsigned short **rows;
rows = (unsigned short**)png_get_rows(png_ptr, png_info);

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.

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.

c - segmentation fault assigning to 2d array

I'm trying to take a bmp file and make a grayscale copy. I'm learning about dynamic allocation and I have to dynamically allocate a 2D array which I import the bmp file to and manipulate it from, so it will work with various picture sizes. But near the end (I labeled where) I get a Seg Fault and I have no idea why. Everything works great if I don't dynamically allocate "pixels".
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const int HEADER_SIZE = 54;
FILE *infile = fopen("test1.bmp", "rb");
FILE *outfile1 = fopen("copy1.bmp", "wb");
int i, width, height, r, c, bmpsize;
char header[HEADER_SIZE], filename[32];
char **pixels; //initialize pixels here
puts("Enter the filename: ");
i = -1;
while(filename[i] != '\n')
{
i++;
scanf("%c", &filename[i]);
}
filename[i] = '.';
filename[i+1] = 'b';
filename[i+2] = 'm';
filename[i+3] = 'p';
filename[i+4] = '\0';
i = -1;
while(filename[i] != '\0')
{
i++;
printf("%c", filename[i]);
}
infile = fopen(filename, "rb");
puts("Enter the height and width (in pixels): ");
scanf("%d%d", &height, &width);
bmpsize = 3 * width * height;
pixels = malloc(height * sizeof(char*)); //DA part 1
for(i = 0; i < height; i++)
{
pixels[i] = malloc((width * 3) * sizeof(char)); //DA part 2
}
fread(header, 1 , HEADER_SIZE, infile);
fread(pixels, 1 , bmpsize, infile);
for( r = 0; r < height; r++) {
for ( c = 0; c < width*3; c += 3) {
int avg= 0;
puts("THIS PUTS PRINTS: THE NEXT LINE IS MY PROBLEM");
avg = ((int) pixels[r][c] + (int) pixels[r][c+1] + (int) pixels[r][c+2]) / 3;//This is my problem line, why?
puts("THIS PUTS DOESN'T PRINT. ERROR: SEG. FAULT(core dumped)");
pixels[r][c] = (char) avg;
pixels[r][c+1] = (char) avg;
pixels[r][c+2] = (char) avg;
}
}
puts("Done. Check the generated images.");
fwrite(header, sizeof(char) , HEADER_SIZE, outfile1);
fwrite(pixels, sizeof(char) , bmpsize, outfile1);
fclose(infile);
fclose(outfile1);
return 0;
}
I think the problem is with your fread call and how you are setting up the input buffer for the file contents
pixels = malloc(height * sizeof(char*)); //DA part 1
for(i = 0; i < height; i++)
{
pixels[i] = malloc((width * 3) * sizeof(char)); //DA part 2
}
fread(pixels, 1 , bmpsize, infile);
The memory you have allocated in total is bmpsize bytes but the length of pixels is just height bytes - and yet you are passing it to fread as if it were a buffer of bmpsize bytes in length. Each element in pixels is a char* - the address of each is block of dynamically allocated array but this does not mean that you can treat your pixels array as a contiguous block of memory.
These arrays allocated dynamically in your loops are thus not initialised which could be leading to the segfaults when you read from them later in your loop (reading uninitialised variables is undefined behaviour).
This probably explains why your code works when you use a non dynamically allocated 2D array - because such a 2D array is a contiguous in memory.
The answer of mathematician1975 is almost correct, since you are accessing data from outside of the area you've allocated, but the last index of c in the inner loop is not 3*width-1 nor 3*width-3, but it's actually width*3.
You should adapt the inner loop to:
for ( c = 0; c < width*3-3; c += 3)
This way c will go until width*3-3 and then you can do c, c+1 and c+2 and you will visit the last bytes of the lines with c+2.

Weird results when implementing Sobel Filter [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have been learning about computer vision and wanted to implement some simple techniques in C. For the first technique, I am doing the Sobel edge detection filter. I understand how it works and so I thought it should be fairly easy to code, but I am getting very weird results.
I am using the following image:
and getting this as a result
New Results!:
It should be noted that I am using the .ppm image format (the links are to jpgs since I could not find an image host that supports .ppm)
Anyways, here is the section of my code that implements Sobel:
/**********************************************************
This program takes in an image file and applies the Sobel
Filter edge detection technique to it.
**********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ppmReader.h"
void sobelFilter(){
//Sobel kernels dx (horizontal) and dy (vertical)
int horizFilter[3][3] = {{ 1, 0, -1},
{ 2, 0, -2},
{ 1, 0, -1}};
int vertFilter[3][3] = {{ 1, 2, 1},
{ 0, 0, 0},
{-1, -2, -1}};
int pixVal = 0;
int horizPixVal = 0;
int vertPixVal = 0;
int x, y, i, j;
//Quick check to make sure dimensions are correct
printf("Using a Width of: %d\n", width);
printf("Using a Height of: %d\n\n", height);
//Start filtering process here
for(x = 0; x < width; x++){
for(y = 0; y < height; y++){
pixVal = 0;
horizPixVal = 0;
vertPixVal = 0;
if(!((x == 0) || (x == width-1) || (y == 0) || (y == height-1))){ //If the current pixel is along the border, ignore it and set to zero
for(i = -1; i <= 1; i++){ //because the kernel does not align to it
for(j = -1; j <= 1; j++){
horizPixVal += (int)(image[y + j][x + i][0]) * horizFilter[i + 1][j + 1]; //Only need to focus on one of the RGB values since the output is
vertPixVal += (int)(image[y + j][x + i][0]) * vertFilter[i + 1][j + 1]; //greyscale and all three values are the same
}
}
}
pixVal = sqrt((horizPixVal * horizPixVal) + (vertPixVal * vertPixVal)); //Calculate magnitude
pixVal = sqrt(horizPixVal * horizPixVal);
if(pixVal > 255) pixVal = 255; //Clamp value within 8-bit range
filteredImage[y][x][0] = (unsigned char)pixVal;
}
}
}
Here is the code that reads the .ppm file:
unsigned char image[MAX_IMAGE_HEIGHT][MAX_IMAGE_WIDTH][3];
unsigned char filteredImage[MAX_IMAGE_HEIGHT][MAX_IMAGE_WIDTH][3];
void readPPMImageData(){
char fileName[MAX_NAME];
char imageBuff[MAX_BUFF];
width = 0;
height = 0;
maxColor = 0;
int x;
int y;
FILE* file;
printf("------------------------------------------------------------\n");
printf("Now attempting to read in the .ppm image file data...\n");
printf("------------------------------------------------------------\n\n");
printf("What is the image file name (*.ppm)? : ");
scanf("%s", fileName);
file = fopen(fileName, "rb"); //open the file specified by the user in binary read mode
if(file == NULL){ //but if the file was not found, terminate program
printf("\nThe file %s could not be found! Terminating program...\n", fileName);
exit(1);
}
//The first step is to read in the file type and check it agains P6 (file type of .ppm images)
fgets(imageBuff, MAX_BUFF, file);
if(imageBuff[0] != 'P' || imageBuff[1] != '6'){
printf("\nInvalid image type! Acceptable type is: %s --- Received type is: %c%c\n\n", "P6", imageBuff[0], imageBuff[1]);
}
printf("Magic Number is: %c%c\n", imageBuff[0], imageBuff[1]);
while(width == 0 || height == 0){
fgets(imageBuff, MAX_BUFF, file);
if(imageBuff[0] != '#') {
sscanf(imageBuff, "%d %d", &width, &height);
}
}
printf("Width is: %d\n", width);
printf("Height is: %d\n", height);
//if(feof(file)){
//
//}
while(maxColor == 0){
fgets(imageBuff, MAX_BUFF, file);
if(imageBuff[0] != '#') {
sscanf(imageBuff, "%d", &maxColor);
}
}
printf("Maximum color value is: %d\n", maxColor);
for(x = 0; x < width; x++){
for(y = 0; y < height; y++){
image[y][x][0] = (unsigned char)fgetc(file); //Get Red value
image[y][x][1] = (unsigned char)fgetc(file); //Get Green value
image[y][x][2] = (unsigned char)fgetc(file); //Get Blue value
}
}
printf("Finished reading image data!\n\n");
fclose(file);
}
And here is the code that creates the new .ppm file after filtering:
void createPPMImage(){
char fileName[MAX_NAME];
FILE* file;
int x;
int y;
printf("------------------------------------------------------------\n");
printf("Now attempting to create new .ppm image file...\n");
printf("------------------------------------------------------------\n\n");
printf("What is the name of the output image file (*.ppm)? : ");
scanf("%s", fileName);
printf("Width is: %d\n", width);
printf("Height is: %d\n", height);
printf("Maximum color value is: %d\n", maxColor);
file = fopen(fileName, "wb");
fputs("P6\n", file);
fprintf(file, "%d %d\n", width, height);
fprintf(file, "%d\n", maxColor);
for(x = 0; x < width; x++){
for(y = 0; y < height; y++){
fputc(filteredImage[y][x][0], file); //Write Red value
fputc(filteredImage[y][x][0], file); //Write Green value
fputc(filteredImage[y][x][0], file); //Write Blue value
}
}
printf("Finished creating new filtered image!\n\n");
fclose(file);
}
I'm 100% sure the issue isn't with the reading or writing of the image as I tested those functions without the filter applied and only get issues once I use the above function.
Any help is appreciated because as far as I can see, the indexing/formula seems to be correctly implemented but that is obviously not true.
EDIT: As Dave and others have pointed out, I am no longer 100% sure that the error is within the Sobel function and it appears this is just some indexing mistake I have made when using the .ppm format. I went ahead and posted the code for my .ppm reader/writer functions and the new results I am getting after applying the [y][x][color] scheme propsed by anatolyg below. I am sorry if my post is way too long and if it is please let me know as this is my first post and I am not entirely sure what is proper yet.
Images are usually indexed with y coordinate first and x second, like this:
... image[y + j][x + i] ...
This is a convention that keeps people from getting confused when dealing with images in C. Unfortunately, it kinda contradicts the one that Matlab uses, so I just hope you are doing it all in C.
In addition, PPM format specification says that the red/green/blue values are interleaved, so the "colour plane" must be the last index:
... image[y + j][x + i][0] ...
unless there was some reordering of the input file while loading it into memory. You didn't show code that reads from the file, so it's hard to know whether it did any reordering.
Addition: reading and writing the file should follow raster ordering, that is, finish pixels of one line before proceeding to next line:
for(y = 0; y < height; y++){
for(x = 0; x < width; x++){
...
}
}
It is also recommended to do processing in this manner; this is not an absolute must, but it will reduce confusion, and might make your processing faster in addition (by using CPU cache more effectively).

Resources