Before those of yours struct
are confused. For example, its structure Elem
does not seem to represent anything that has any real value. Ideally the image would contain directly the pixels and nothing else. The function that creates an image is called criar_lista()
, what’s confusing.
Reading the image values are also wrong, because if you are reading a int
, should wear sizeof(int)
and not sizeof(IMAGEM)
. Incidentally, fread
is to read data in binary format (see more about this in that other answer of mine). But in your case, the format is textual. So use fscanf
or fgets
.
Another thing is that it is necessary to maintain the consistency of the language nomenclature rules. Therefore, variable names and fields must be lowercase.
The first step is the #include
s:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
The structures we need are the PIXEL
and the IMAGEM
that contains them:
typedef struct PIXEL {
char r, g, b; // O tipo char sempre ocupa 1 byte na memória.
} PIXEL;
typedef struct IMAGEM {
int linhas, colunas;
PIXEL *pixels;
} IMAGEM;
Then a function to create a IMAGEM
, destroy it and access its pixels:
IMAGEM *criar_imagem(int linhas, int colunas) {
IMAGEM *im = (IMAGEM *) malloc(sizeof(IMAGEM));
im->inicio = pixels;
im->linhas = linhas;
im->colunas = colunas;
im->pixels = (PIXEL *) malloc(linhas * colunas * sizeof(PIXEL));
return im;
}
PIXEL *acessar_pixel(IMAGEM *im, int linha, int coluna) {
return &(im->pixels[linha * im->colunas + coluna]);
}
void destruir_imagem(IMAGEM *im) {
free(im->pixels);
free(im);
}
To read the contents of the different line types of a file, we can do these functions:
int ler_string(FILE *f, char *dst, int s) {
return fgets(dst, s, f) != NULL;
}
int ler_tres_ints(FILE *f, int *a, int *b, int *c) {
char buffer[200];
if (!ler_string(f, buffer, 200)) return 0;
char *end_a, *end_b, *end_c;
errno = 0;
int x = strtol(buffer, &end_a, 10);
if (end_a == &buffer || errno || *end_a != ' ') return 0;
int y = strtol(&(end_a[1]), &end_b, 10);
if (end_b == end_a || errno || *end_b != ' ') return 0;
int z = strtol(&(end_b[1]), &end_c, 10);
if (end_c == end_b || errno || *end_c != 0) return 0;
*a = x;
*b = y;
*c = z;
return 1;
}
int ler_int(FILE *f, int *a) {
char buffer[200];
if (!ler_string(f, buffer, 200)) return 0;
char *end;
errno = 0;
int x = strtol(buffer, &end, 10);
if (end == &buffer || errno || *end != 0) return 0;
*a = x;
return 1;
}
Finally, to read the contents of a file image, we can do this:
IMAGEM *ler_imagem_do_ficheiro(const char *nome) {
// 1. Abre o ficheiro. Retorna NULL se não conseguir abri-lo.
FILE *f = fopen(nome, "rb");
if (f == NULL) return NULL; // Erro ao abrir o ficheiro.
// 2. Obtém o tamanho do ficheiro aberto.
fseek(f, 0L, SEEK_END);
int sz = ftell(f);
fseek(f, 0L, SEEK_SET);
// 3. Lê o nome da imagem, e o descarta, pois não precisamos dela.
char nome[200];
ler_string(f, nome, 200);
// 4. Lê o número de linhas, colunas e canais do ficheiro.
int linhas = 0, colunas = 0, canais = 0;
int a = ler_tres_ints(f, &linhas, &colunas, &canais); // Lê os números.
// 5. Se não conseguiu ler o número de linhas, colunas e/ou canais, então o
// ficheiro está incompleto. Fecha-o e retorna NULL. O mesmo ocorre se o número
// de canais for diferente de 3.
if (!a || canais != 3) {
fclose(f);
return NULL; // Erro: Ficheiro incompleto ou número de canais diferente de 3.
}
// 6. Cria a imagem na memória.
IMAGEM *im = criar_imagem(linhas, colunas);
// 7. Lê todos os pixels da imagem.
for (int linha = 0; linha < linhas; linha++) {
for (int coluna = 0; coluna < colunas; coluna++) {
// 7.1. Lê o conteúdo do pixel.
PIXEL *p = acessar_pixel(im, linha, coluna);
int x = ler_int(f, &(p->r));
int y = ler_int(f, &(p->g));
int z = ler_int(f, &(p->b));
// 7.2. Se não conseguiu ler o pixel, então o ficheiro está
// incompleto. Fecha-o, destrói a imagem incompleta e retorna NULL.
if (!x || !y || !z) {
fclose(f);
destruir_imagem(im);
return NULL; // Erro: Ficheiro incompleto.
}
}
}
// 8. Verifica se chegou no final do ficheiro e o fecha. Se não tiver, então
// há dados excedentes após o último pixel. Considera isso como uma
// imagem defeituosa/corrompida, e portanto a destrói, fecha o ficheiro.
a = ftell(f);
fclose(f);
if (a != sz) {
destruir_imagem(im);
return NULL; // Erro: Há dados excedentes após o que seria o fim da imagem.
}
// 9. Se conseguiu abrir o ficheiro, ler todos os pixels e fechar sem que nenhum
// erro tivesse ocorrido, então a imagem está pronta e deve ser retornada.
return im;
}
It is important to note that the format of this file is important:
A line containing the image name. However, the image name is disregarded.
A line with three decimal numbers separated by a single space with no spaces before the first number or after the last. These numbers are the number of rows, the number of columns and the number of channels in the image. The number of channels should be 3.
Multiple numbers representing pixel components, each in a row. They are presented in the order R, G and B, so that each pixel is represented by three numerical lines.
There should be nothing but the data on the last pixel.
Three int
s representing the width, height and number of channels followed by the pixels in the order R, G and B, one byte for each component. It is important that the file contains all pixels and that there is nothing more than the last pixel. Also, so the function does not reject the input file and returns NULL
, all three int
s from the beginning of the file must have the endianess correct, the size of each of them has to be sizeof(int)
and the number of channels should be 3. The image name is not shown inside the file.
The next step is to find the zones. To do this, let’s create a structure similar to the image. However, instead of storing the colors of each pixel, it stores the zone number:
typedef struct ZONEAMENTO {
int linhas, colunas, zonas;
int *pixels;
} ZONEAMENTO;
Once this is done, we will need functions to create, destroy and access the zone numbers of this structure:
ZONEAMENTO *criar_zoneamento(int linhas, int colunas) {
ZONEAMENTO *z = (IMAGEM *) malloc(sizeof(ZONEAMENTO));
z->inicio = pixels;
z->linhas = linhas;
z->colunas = colunas;
z->zonas = 0;
z->pixels = (int *) calloc(linhas * colunas, sizeof(int));
return z;
}
int acessar_numero_de_zona(ZONEAMENTO *z, int linha, int coluna) {
return z->pixels[linha * z->colunas + coluna];
}
int definir_numero_de_zona(ZONEAMENTO *z, int linha, int coluna, int zona) {
z->pixels[linha * z->colunas + coluna] = zona;
}
void destruir_zoneamento(ZONEAMENTO *z) {
free(z->pixels);
free(z);
}
Once that’s done, we can start mapping the zones:
ZONEAMENTO *calcular_zonas(IMAGEM *im, int r, int g, int b, int d) {
ZONEAMENTO *z = criar_zoneamento(im->linhas, im->colunas);
// Percorre cada pixel da imagem.
for (int i = 0; i < im->linhas; i++) {
for (int j = 0; j < im->colunas; j++) {
// Tenta marcar a zona do pixel e de todo o blob a ele pertencente.
// Se conseguir, contabiliza mais uma zona.
if (marcar_zona(im, z, i, j, r, g, b, d)) {
z->zonas++;
}
}
}
return z;
}
Now, let’s see this function marcar_zona
:
int marcar_zona(IMAGEM *im, ZONEAMENTO *z, int i, int j, int r, int g, int b, int d) {
// Se o pixel está fora do limite da imagem, não marca nada.
if (i < 0 || j < 0 || i >= im->linhas || j >= im->colunas) return 0;
// Se este pixel já está marcado com algum número de zona, pule ele.
if (acessar_numero_de_zona(z, i, j) != 0) return 0;
// Obtém o pixel e suas cores.
PIXEL *p = acessar_pixel(im, i, j);
// Se este pixel tem uma cor muito diferente da que é esperada, pule ele.
if (abs(p->r - r) > d || abs(p->g - g) > d || abs(p->b - b) > d) return 0;
// Se chegamos aqui, marca o pixel como pertencente
// a uma nova zona que está se formando.
definir_numero_de_zona(im, i, j, z->zonas + 1);
// Marca recursivamente os pixels vizinhos.
marcar_zona(im, z, i + 1, j, r, g, b, d);
marcar_zona(im, z, i - 1, j, r, g, b, d);
marcar_zona(im, z, i, j + 1, r, g, b, d);
marcar_zona(im, z, i, j - 1, r, g, b, d);
// Retorna que a zona foi marcada com sucesso.
return 1;
}
Where is the code that defines what is
BLOCO
and what’s inside ofcriarmatriz
? What is the number of image channels? This file format has been set by you or not?– Victor Stafusa
I have a half written answer, but I don’t understand some of the details of exactly what you intend to do to create these zones. Do pixels of the same color in different regions belong to the same areas? If you have a color zone (0, 128, 0) and another (0, 192, 0), and both have as distance 50, where you would put a color pixel (0, 160, 0)?
– Victor Stafusa
@Victorstafusa Block is the structure that will receive the lists of blobs. The function creaturematrix, will create an array that stores the pixels of the image. for example, m[0][0] stores the 1st pixel. the number of channels, in this case 3 means that each pixel has 3 channels, R, G and B. The pixel file is already given to us.
– IanMoone
@Victorstafusa the function of calculating zones has as input parameters an R, G and B and a Deviation. These RGB are inserted by the user, and all pixels are supposed to be compared to that RGB with the offset, to see if they are similar or not. If a pixel is similar, check the neighboring pixels. If they are similar, a zone (blob) is created, and both are part of the same zone. In this case the neighborhood is the pixel of the top, bottom, left and right row of the pixel that is similar to the RGB given in the function
– IanMoone
But I already knew that. My question is whether two areas of the same color, however distant, are parts of the same zone or not.
– Victor Stafusa
Is the file name really inside the file? If so, how do you know where the name ends and the number of lines starts? The number of rows, number of columns and number of channels are represented with how many bits? Are little-endian or big-endian?
– Victor Stafusa
@Victorstafusa no, two areas of the same color, but distant are different zones. Inside the file, the first line contains the image name, the second the image size.
– IanMoone
Can you edit the question and post the contents of the file to it? If it is too large, put it on an external link. Then I adjust the answer to agree.
– Victor Stafusa
The file has about 3 images, which gives a total of 18 million lines, but it is basically the same. I will then put a part of the file. each pixel contains the values of 3 lines. Each line is intended for R or G or B.
– IanMoone
I edited the answer. Test it and tell me if it works. And if it doesn’t, tell me where it went wrong if possible.
– Victor Stafusa