Converting 1-bit bmp file to array in C/C++ [closed] - c++

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I'm looking to turn a 1-bit bmp file of variable height/width into a simple two-dimensional array with values of either 0 or 1. I don't have any experience with image editing in code and most libraries that I've found involve higher bit-depth than what I need. Any help regarding this would be great.

Here's the code to read a monochrome .bmp file
(See dmb's answer below for a small fix for odd-sized .bmps)
#include <stdio.h>
#include <string.h>
#include <malloc.h>
unsigned char *read_bmp(char *fname,int* _w, int* _h)
{
unsigned char head[54];
FILE *f = fopen(fname,"rb");
// BMP header is 54 bytes
fread(head, 1, 54, f);
int w = head[18] + ( ((int)head[19]) << 8) + ( ((int)head[20]) << 16) + ( ((int)head[21]) << 24);
int h = head[22] + ( ((int)head[23]) << 8) + ( ((int)head[24]) << 16) + ( ((int)head[25]) << 24);
// lines are aligned on 4-byte boundary
int lineSize = (w / 8 + (w / 8) % 4);
int fileSize = lineSize * h;
unsigned char *img = malloc(w * h), *data = malloc(fileSize);
// skip the header
fseek(f,54,SEEK_SET);
// skip palette - two rgb quads, 8 bytes
fseek(f, 8, SEEK_CUR);
// read data
fread(data,1,fileSize,f);
// decode bits
int i, j, k, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for(i = 0 ; i < w / 8; i++) {
int fpos = j * lineSize + i, pos = rev_j * w + i * 8;
for(k = 0 ; k < 8 ; k++)
img[pos + (7 - k)] = (data[fpos] >> k ) & 1;
}
}
free(data);
*_w = w; *_h = h;
return img;
}
int main()
{
int w, h, i, j;
unsigned char* img = read_bmp("test1.bmp", &w, &h);
for(j = 0 ; j < h ; j++)
{
for(i = 0 ; i < w ; i++)
printf("%c ", img[j * w + i] ? '0' : '1' );
printf("\n");
}
return 0;
}
It is plain C, so no pointer casting - beware while using it in C++.
The biggest problem is that the lines in .bmp files are 4-byte aligned which matters a lot with single-bit images. So we calculate the line size as "width / 8 + (width / 8) % 4". Each byte contains 8 pixels, not one, so we use the k-based loop.
I hope the other code is obvious - much has been told about .bmp header and pallete data (8 bytes which we skip).
Expected output:
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
0 0 0 0 0 0 1 1 1 1 0 0 1 1 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 1 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 1 0 0 0
0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 1 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 0 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0

I tried the solution of Viktor Lapyov on a 20x20 test image:
But with his code, I get this output (slightly reformatted but you can see the problem):
The last 4 pixels are not read. The problem is here. (The last partial byte in a row is ignored.)
// decode bits
int i, j, k, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for(i = 0 ; i < w / 8; i++) {
int fpos = j * lineSize + i, pos = rev_j * w + i * 8;
for(k = 0 ; k < 8 ; k++)
img[pos + (7 - k)] = (data[fpos] >> k ) & 1;
}
}
I rewrote the inner loop like this:
// decode bits
int i, byte_ctr, j, rev_j;
for(j = 0, rev_j = h - 1; j < h ; j++, rev_j--) {
for( i = 0; i < w; i++) {
byte_ctr = i / 8;
unsigned char data_byte = data[j * lineSize + byte_ctr];
int pos = rev_j * w + i;
unsigned char mask = 0x80 >> i % 8;
img[pos] = (data_byte & mask ) ? 1 : 0;
}
}
and all is well:

The following c code works with monochrome bitmaps of any size. I'll assume you've got your bitmap in a buffer with heights and width initialized from file. So
// allocate mem for global buffer
if (!(img = malloc(h * w)) )
return(0);
int i = 0, k, j, scanline;
// calc the scanline. Monochrome images are
// padded with 0 at every line end. This
// makes them divisible by 4.
scanline = ( w + (w % 8) ) >> 3;
// account for the paddings
if (scanline % 4)
scanline += (4 - scanline % 4);
// loop and set the img values
for (i = 0, k = h - 1; i < h; i++)
for (j = 0; j < w; j++) {
img[j+i*w] = (buffer[(j>>3)+k*scanline])
& (0x80 >> (j % 8));
}
Hope this help's. To convert it to 2D is now a trivial matter: But if u get lost here is the math to convert 1D array to 2D suppose r & c are row and column and w is the width then:
. c + r * w = r, c
If you got further remarks hit me back, am out!!!

Lets think of a1x7 monochrome bitmap i.e. This is a bitmap of a straight line with 7 pixels wide. To store this image on a Windows OS; since 7 is not evenly divisible by 4 it's going to pad in it an extra 3 bytes.
So the biSizeImage of the BITMAPINFOHEADER structure will show a total of 4 bytes. Nonetheless the biHeight and biWidth members will correctly state the true bitmap dimensions.
The above code will fail because 7 / 8 = 0 (by rounding off as with all c compilers do). Hence loop "i" will not execute so will "k".
That means the vector "img" now contains garbage values that do not correspond to the pixels contained in " data" i.e. the result is incorrect.
And by inductive reasoning if it does not satisfy the base case then chances are it wont do much good for general cases.

Related

gltf, are matrices ever to be transposed?

As I am learning gltf, I already have 2 working skin models, and now I am trying the RiggedFigure.
The 2 other models worked just fine and I am using the same code. I am using the vscode gltf extension to verify my output.
The documentation states:
Accessors of matrix type have data stored in column-major order; start of each column must be aligned to 4-byte boundaries.
Eigen matrices are also column major, thus copying the raw bytes into an stl vector of type Eigen::Matrix4f should result in the correct data, and indeed, this is the case for 2 of the 3 models I have tried so far.
However for the rigged figure, vs code says the matrices should be (excuse the screenshot but I cannot copy paste the matrices for some reason):
My code prints:
0.999983 0.000442018 0.00581419 -0.00398856
0 0.997123 -0.0758045 0.0520021
-0.005831 0.0758032 0.997106 -0.684015
0 0 0 1
1 0 0 0
0 -0.01376 0.999905 -0.85674
0 -0.999905 -0.0137601 0.024791
0 0 0 1
1 0 0 0
0 0.979842 0.199774 -0.224555
0 -0.199774 0.979842 -1.05133
0 0 0 1
1 0 0 0
0 -0.00751853 0.999972 -1.12647
0 -0.999972 -0.00751847 0.00796944
0 0 0 1
-1 -1.50995e-07 0 0
0 0.00364935 0.999993 -1.19299
-1.51869e-07 0.999993 -0.00364941 0.00535393
0 0 0 1
-0.0623881 0.998036 -0.00569177 0.00162297
0.891518 0.0531644 -0.449853 0.404156
-0.448667 -0.0331397 -0.893084 0.998987
0 0 0 1
0.109672 0.988876 -0.100484 0.107683
-0.891521 0.0531632 -0.449849 0.404152
-0.439503 0.13892 0.887434 -0.993169
0 0 0 1
0.530194 0.847874 0.001751 -0.183428
0.760039 -0.474352 -0.444218 0.206564
-0.375811 0.236853 -0.895917 0.973213
0 0 0 1
-0.0705104 -0.619322 0.781965 -0.761146
-0.760038 -0.474352 -0.444223 0.206569
0.646043 -0.625645 -0.437261 0.633599
0 0 0 1
0.631434 0.775418 -0.00419003 -0.228155
0.649284 -0.53166 -0.543845 0.154659
-0.423935 0.340682 -0.839175 0.951451
0 0 0 1
0.111378 -0.773831 0.623523 -0.550204
-0.649284 -0.531661 -0.543845 0.15466
0.752347 -0.344271 -0.561651 0.809067
0 0 0 1
-0.830471 -0.549474 0.091635 -0.00030848
0.0339727 -0.214148 -0.97621 0.596867
0.556025 -0.807601 0.196511 -0.159297
0 0 0 1
-0.994689 0.102198 0.0121981 -0.0750653
-0.0339737 -0.214147 -0.97621 0.596867
-0.0971548 -0.97144 0.216482 -0.140501
0 0 0 1
-0.99973 0.0232223 -7.82996e-05 0.0784336
0.0051282 0.217484 -0.97605 0.357951
-0.0226493 -0.975788 -0.217544 0.0222206
0 0 0 1
-0.998171 -0.0599068 -0.00810355 -0.0775425
-0.00512856 0.217484 -0.97605 0.357951
0.0602345 -0.974224 -0.217393 0.0251548
0 0 0 1
-0.999327 0.0366897 0 0.0783684
0.0287104 0.781987 0.622632 -0.0567413
0.0228442 0.622213 -0.782514 0.0634761
0 0 0 1
-0.999326 0.00828946 0.0357652 -0.0814984
0.0287402 0.782804 0.621604 -0.0521458
-0.0228444 0.622213 -0.782514 0.0634761
0 0 0 1
0.994013 0.109264 0.000418345 -0.0755577
0.109252 -0.993835 -0.0188101 -0.0405796
-0.00164008 0.0187438 -0.999822 0.0227357
0 0 0 1
0.994011 -0.109281 0.000483894 0.0755372
-0.109253 -0.993836 -0.018811 -0.0405797
0.00253636 0.0186453 -0.999823 0.0228038
0 0 0 1
Which are the transposed versions of what vs code says.
My loading code is this (instantiated with typoe Eigen::Matrix4f):
void CopySparseBuffer(
void* dest,
const void* src,
const size_t element_count,
const size_t stride,
const size_t type_size)
{
assert(stride >= type_size);
// Typecast src and dest addresses to (char *)
unsigned char* csrc = (unsigned char*)src;
unsigned char* cdest = (unsigned char*)dest;
// Iterate over the total number of elements to copy
for(int i = 0; i < element_count; i++)
// Copy each byte of the element. Since the stride could be different from the
// type size (in the case of padding bytes for example) the right access
// should skip over any interleaved data, that's why we use the stride.
for(int j = 0; j < type_size; j++)
*(cdest + i * type_size + j) = *(csrc + i * stride + j);
}
template<typename T>
std::vector<T> ExtractDataFromAccessor(
const tinygltf::Model& model, const int accessor_index, bool print = false)
{
const int buffer_view_index = model.accessors[accessor_index].bufferView;
const int array_type = model.accessors[accessor_index].type;
const int component_type = model.accessors[accessor_index].componentType;
const int accessor_offset = model.accessors[accessor_index].byteOffset;
const int element_num = model.accessors[accessor_index].count;
const int buffer_index = model.bufferViews[buffer_view_index].buffer;
const int buffer_length = model.bufferViews[buffer_view_index].byteLength;
const int buffer_offset = model.bufferViews[buffer_view_index].byteOffset;
const int buffer_stride = model.bufferViews[buffer_view_index].byteStride;
const std::vector<unsigned char> data = model.buffers[buffer_index].data;
assert(
component_type == ComponentCode<T>() &&
"The component type found here should match that of the type (e.g. float and "
"float).");
assert(array_type == TypeCode<T>());
// Size in bytes of a single element (e.g. 12 for a vec3 of floats)
const int type_size = sizeof(T);
assert(
buffer_stride == 0 || buffer_stride >= sizeof(T) &&
"It doesn't make sense for a positive buffer "
"stride to be less than the type size");
assert(element_num * type_size <= buffer_length);
const size_t stride = std::max(buffer_stride, type_size);
std::vector<T> holder(element_num);
CopySparseBuffer(
holder.data(),
data.data() + buffer_offset + accessor_offset,
element_num,
stride,
type_size);
return holder;
}
I just figured it out so i will leave this here in case someone is in the same situation in the future.
The VS code vectors are the columns, not the rows, so my code and vs code actually agree, it's just the vs code output is confusing.
In short, evrything works, the output is just confusing.

Finding boundaries of figure in 2d array

There is a figure that is represented by 1 values that are “connected” vertically, horizontally or diagonally in a 2 dementional array.
I need to save the index of the boundary of the figure (the row and column of the 0's that are connected to the figure, in any type of c++ container.
For instance, in the following 2d array, I should get the following indexes:
(0,2), (0,3), (0,4), (1,2), (1,4), (1,5), (2,2), (2,3), (2,5), (2,6)... etc.
0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 1 1 1 0 0
0 0 0 0 1 1 0 0
0 0 0 1 1 1 0 0
0 0 0 1 1 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
What is the most efficient way to do so, on both space and time complexity?
void dfs(vector<vector<int>>& matrix, vector<vector<int>>& boundary, int rows, int cols, int i, int j){
if(!isValidCoordinate(i, j))
return;
if(isAnyNeighborOne(i, j)){
boundary.push_back({i, j});
matrix[i][j] = 2;
}
else
matrix[i][j] = 3;
//Explore eight directions
/* I didn't bother about x = 0 and y = 0.
* You can, if you want.
* Doesn't make a difference though.
*/
for(int x = -1; x < 2; x++){
for(int y = -1; y < 2; y++){
dfs(matrix, boundary, rows, cols, i + x, i + y);
}
}
}
vector<vector<int>> getBoundary(vector<vector<int>>& matrix){
vector<vector<int>> boundary;
int rows = matrix.size();
if(!rows)
return boundary;
int cols = matrix[0].size();
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
if(matrix[i][j] == 0){
dfs(matrix, boundary, rows, cols, i, j);
}
}
}
return boundary;
}
If you print the matrix at the end, you'll see the boundary with 2.
Whatever you see as 3, if you want, you can set it back to 0.
isValidCoordinate() and isAnyNeighborOne() is left to you as an exercise.
I use vector<vector<int>> for boundary. You can try using vector<pair<int,int>> as well.
With the above solution you'll get inner boundary as well as outer boundary. As an exercise, you can try only inner boundary or only outer boundary.
You can solve the same problem with BFS as well. If the matrix is of large size, stack might overflow due to recursive calls. Better to prefer BFS in such cases.
Time and space complexity of the above solution is O(rows * cols).

Inverting bits of PBM image while vs for loop

I am trying to flip the color of pixels of a simple pbm image which has only pure black and pure white. I am generating the image myself and then reading it and then flipping the bits and saving both the generated image and the color inverted image.
Here is my code (write_pbm.cpp) -
#include "headers/write_pbm.h"
int width_pbm, height_pbm;
void input_sample_dim(){
printf("Enter the width and height of image to create = ");
scanf("%d %d", &width_pbm, &height_pbm);
}
void create_sample_image(){
FILE *fp;
fp = fopen("sample.pbm", "wb");
fprintf(fp, "P1\n");
fprintf(fp, "# myfile.pbm\n");
fprintf(fp, "%d %d\n", width_pbm, height_pbm);
for (int i = 1; i <= height_pbm; i++)
{
for (int j = 1; j <= width_pbm; j++)
{
if (j == i || (width_pbm - j + 1 == i))
fprintf(fp, "0");
else
fprintf(fp, "1");
if (j == width_pbm)
fprintf(fp, "\n");
else
fprintf(fp, " ");
}
}
fclose(fp);
}
void invert (){
printf("\tinverting the image\nturning black pixels white and white pixels black\n");
FILE *fp = fopen("sample.pbm", "rb");
while(fgetc(fp) != '\n');
while(fgetc(fp) != '\n');
while(fgetc(fp) != '\n');
FILE *fp_inv;
fp_inv = fopen("inverted.pbm", "wb");
fprintf(fp_inv, "P1\n");
fprintf(fp_inv, "# inverted.pbm\n");
fprintf(fp_inv, "%d %d\n", width_pbm, height_pbm);
for (int i = 1; i <= height_pbm; i++){
for (int j = 1; j <= width_pbm; j++){
char ch = fgetc(fp);
if (ch == '1')
fputc('0', fp_inv);
else if (ch == '0')
fputc('1', fp_inv);
else
fputc(ch, fp_inv);
}}
fclose(fp);
fclose(fp_inv);
}
Below is the header I am including (write_pbm.h)
#ifndef Write_PBM_H
#define Write_PBM_H
#include <cstdio>
#include <iostream>
void create_sample_image(void);
void input_sample_dim(void);
void invert (void);
extern int width_pbm, height_pbm;
#endif
Below is my main -
#include "write/PBM/headers/write_pbm.h"
int main(){
input_sample_dim();
printf("writing sample image\n");
create_sample_image();
printf("sample image with dimenstions %d by %d created\n", width_pbm, height_pbm);
invert();
}
So I am making a V cross kind of pattern and then inverting the colors and saving both of the created image and the inverted image.
Lets suppose we provide input 10 10
then the file sample.pbm looks like
P1
# myfile.pbm
10 10
0 1 1 1 1 1 1 1 1 0
1 0 1 1 1 1 1 1 0 1
1 1 0 1 1 1 1 0 1 1
1 1 1 0 1 1 0 1 1 1
1 1 1 1 0 0 1 1 1 1
1 1 1 1 0 0 1 1 1 1
1 1 1 0 1 1 0 1 1 1
1 1 0 1 1 1 1 0 1 1
1 0 1 1 1 1 1 1 0 1
0 1 1 1 1 1 1 1 1 0
and inverted.pbm looks like this
P1
# inverted.pbm
10 10
1 0 0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0 1 0
0 0 1 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 1 1 0 0 0 0
as you can see that only half the rows are getting printed in the inverted image.
If I replace the nested loops of invert() of write_pbm.cpp with
char ch;
while(!feof(fp))
{
char ch = fgetc(fp);
if (ch == '1')
fputc('0', fp_inv);
else if (ch == '0')
fputc('1', fp_inv);
else
fputc(ch, fp_inv);
}
then it gives the right output in the inverted.pbm file which is
P1
# inverted.pbm
10 10
1 0 0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0 1 0
0 0 1 0 0 0 0 1 0 0
0 0 0 1 0 0 1 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 0 1 1 0 0 0 0
0 0 0 1 0 0 1 0 0 0
0 0 1 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0 1 0
1 0 0 0 0 0 0 0 0 1
\FF
I am doing the same thing through both the nested loops and the while loop then why is it giving the wrong output in case of nested for loops?
Thanks for reading this, Please provide your valuable response.
Looking at your input file, it appears that every meaningful char (i.e. '0' or '1') is followed by a space.
In the while loop that is working, you read as many chars as needed, until the end of file, just inverting the correct chars and copying unexpected chars. So everything is processed.
In the nested for loops, you are reading the exact number of chars corresponding to the dimensions of the picture so 10*10. Taking into account the layout of the input file, you are therefore reading only 100 chars, so 50 '1' or '0' and 50 spaces. So you process only half of the input.
Unrelated: your code uses C++ header, but all the rest is just plain C. In C++, you should consider to use fstream to read your files, and not the C legacy FILE* API.

conway game error with 2d array manipulation

So I'm working on the life game, and so far I have come up with this http://ideone.com/QG4tsS I'm not sure exactly if I am on the right track or not. Basically I have a function putting out random values to try and test my code. But nothing seems to happen. I suspect my problem lies with the following code
int sum = 0;
for (int k = (i - 1); k <= (i + 1); k++) {
for (int l = (j - 1); l <= (j + 1); l++) {
sum += currentGen[k][l];
}
}
return sum;
So my result gives me a 2d array with all 0's but shouldn't I start to see some changes and patterns starting to form? I get one 1 and the rest are 0.
Output
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 1
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
I provide this answer based on the code you posted at http://ideone.com/QG4tsS . You really should consider adding that code to your original question, so that future folks who find this on StackOverflow have the full context.
Your RandomCells function only sets cells to 1 if they meet the RANDOM threshold. It doesn't clear them to 0 otherwise. Once you fix that, you'll be all set. ie.
void RandomCells(int currentGen[][CELLY]) {
for (int i = 0; i < CELLX; i++) {
for (int j = 0; j < CELLY; j++) {
if (rand() % 100 + 1 < RANDOM) {
currentGen[i][j] = 1;
} else
{
currentGen[i][j] = 0;
}
}
}
}
Without that else clause, I was seeing initial generations that looked like this:
0 0 4196155 1
1813657216 1 4197653 0
-870503576 1 4197584 1
Clearly, most of those cells were non-zero, and so Conway's Life algorithm would map them to 0 in the next generation because of "crowding".
The reason currentGen was filled with such 'random' values is that it was allocated as an automatic variable to main. Automatic variables do not get initialized to any particular value. You need to initialize them yourself. You can do that by modifying your algorithm (as I did above), or by adding an explicit bit of code to initialize the structure.
This differs from file-scope variables, which C and C++ define as initialized-to-zero on program start if they don't have initializers or default constructors. (Pedants will point out that even that has caveats.)
Once you make the required fixes, to truly see Conway's Life, you'll need to set CELLX and CELLY to larger values...

Image downscaling algorithm

Could you help me find the right algorithm for image resizing? I have an image of a number. The maximum size is 200x200, I need to get an image with size 15x15 or even less. The image is monochrome (black and white) and the result should be the same. That's the info about my task.
I've already tried one algorithm, here it is
// xscale, yscale - decrease/increase rate
for (int f = 0; f<=49; f++)
{
for (int g = 0; g<=49; g++)//49+1 - final size
{
xpos = (int)f * xscale;
ypos = (int)g * yscale;
picture3[f][g]=picture4[xpos][ypos];
}
}
But it won't work with the decrease of an image, which is my prior target.
Could you help me find an algorithm, which could solve that problem (quality mustn't be perfect, the speed doesn't even matter). Some information about it would be perfect too considering the fact I'm a newbie. Of course, a short piece of c/c++ code (or a library) will be perfect too.
Edit:
I've found an algorithm. Will it be suitable for compressing from 200 to 20?
The general approach is to filter the input to generate a smaller size, and threshold to convert to monochrome. The easiest filter to implement is a simple average, and it often produces OK results. The Sinc filter is theoretically the best but it's impractical to implement and has ringing artifacts which are often undesirable. Many other filters are available, such as Lanczos or Tent (which is the generalized form of Bilinear).
Here's a version of an average filter combined with thresholding. Assuming picture4 is the input with pixel values of 0 or 1, and the output is picture3 in the same format. I also assumed that x is the least significant dimension which is opposite to the usual mathematical notation, and opposite to the coordinates in your question.
int thumbwidth = 15;
int thumbheight = 15;
double xscale = (thumbwidth+0.0) / width;
double yscale = (thumbheight+0.0) / height;
double threshold = 0.5 / (xscale * yscale);
double yend = 0.0;
for (int f = 0; f < thumbheight; f++) // y on output
{
double ystart = yend;
yend = (f + 1) / yscale;
if (yend >= height) yend = height - 0.000001;
double xend = 0.0;
for (int g = 0; g < thumbwidth; g++) // x on output
{
double xstart = xend;
xend = (g + 1) / xscale;
if (xend >= width) xend = width - 0.000001;
double sum = 0.0;
for (int y = (int)ystart; y <= (int)yend; ++y)
{
double yportion = 1.0;
if (y == (int)ystart) yportion -= ystart - y;
if (y == (int)yend) yportion -= y+1 - yend;
for (int x = (int)xstart; x <= (int)xend; ++x)
{
double xportion = 1.0;
if (x == (int)xstart) xportion -= xstart - x;
if (x == (int)xend) xportion -= x+1 - xend;
sum += picture4[y][x] * yportion * xportion;
}
}
picture3[f][g] = (sum > threshold) ? 1 : 0;
}
}
I've now tested this code. Here's the input 200x200 image, followed by a nearest-neighbor reduction to 15x15 (done in Paint Shop Pro), followed by the results of this code. I'll leave you to decide which is more faithful to the original; the difference would be much more obvious if the original had some fine detail.
To properly downscale an image, you should divide your image up into square blocks of pixels and then use something like Bilinear Interpolation in order to find the right color of the pixel that should replace the NxN block of pixels you're doing the interpolation on.
Since I'm not so good at the math involved, I'm not going to try give you an example of how the code would like. Sorry :(
Since you're fine with using a library, you could look into the imagemagick C++ bindings.
You could also output the image in a simple format like a pbm, and then call the imagemagick command to resize it:
system("convert input.pbm -resize 10x10 -compress none output.pbm");
Sample output file (note: you don't need to use a new line for each row):
P1
20 20
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
The output file:
P1
10 10
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 1 1 0 1 1 0
0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 1 1 1 1
1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0
I've found an implementation of a bilinear interpolaton. C code.
Assuming that:
a - a primary array (which we need to stretch/compress) pointer.
oldw - primary width
oldh - primary height
b - a secondary array (which we get after compressing/stretching) pointer
neww - secondary width
newh - seconday height
#include <stdio.h>
#include <math.h>
#include <sys/types.h>
void resample(void *a, void *b, int oldw, int oldh, int neww, int newh)
{
int i;
int j;
int l;
int c;
float t;
float u;
float tmp;
float d1, d2, d3, d4;
u_int p1, p2, p3, p4; /* nearby pixels */
u_char red, green, blue;
for (i = 0; i < newh; i++) {
for (j = 0; j < neww; j++) {
tmp = (float) (i) / (float) (newh - 1) * (oldh - 1);
l = (int) floor(tmp);
if (l < 0) {
l = 0;
} else {
if (l >= oldh - 1) {
l = oldh - 2;
}
}
u = tmp - l;
tmp = (float) (j) / (float) (neww - 1) * (oldw - 1);
c = (int) floor(tmp);
if (c < 0) {
c = 0;
} else {
if (c >= oldw - 1) {
c = oldw - 2;
}
}
t = tmp - c;
/* coefficients */
d1 = (1 - t) * (1 - u);
d2 = t * (1 - u);
d3 = t * u;
d4 = (1 - t) * u;
/* nearby pixels: a[i][j] */
p1 = *((u_int*)a + (l * oldw) + c);
p2 = *((u_int*)a + (l * oldw) + c + 1);
p3 = *((u_int*)a + ((l + 1)* oldw) + c + 1);
p4 = *((u_int*)a + ((l + 1)* oldw) + c);
/* color components */
blue = (u_char)p1 * d1 + (u_char)p2 * d2 + (u_char)p3 * d3 + (u_char)p4 * d4;
green = (u_char)(p1 >> 8) * d1 + (u_char)(p2 >> 8) * d2 + (u_char)(p3 >> 8) * d3 + (u_char)(p4 >> 8) * d4;
red = (u_char)(p1 >> 16) * d1 + (u_char)(p2 >> 16) * d2 + (u_char)(p3 >> 16) * d3 + (u_char)(p4 >> 16) * d4;
/* new pixel R G B */
*((u_int*)b + (i * neww) + j) = (red << 16) | (green << 8) | (blue);
}
}
}
Hope it will be useful for other users. But nevertheless I still doubth whether it will work in my situation (when not stratching, but compressing an array). Any ideas?
I think, you need Interpolation. There are a lot of algorithms, for example you can use Bilinear interpolation
If you use Win32, then StretchBlt function possibly help.
The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context.
One approach to downsizing a 200x200 image to, say 100x100, would be to take every 2nd pixel along each row and column. I'll leave you to roll your own code for downsizing to a size which is not a divisor of the original size. And I provide no warranty as to the suitability of this approach for your problem.