How to calculate blobs in C?

Asked

Viewed 413 times

1

How can I calculate a pixel zone in C?

The pixels, R G B are given from a file. The user has to insert an R, G and B and a deviation as it is in the function to calculate zones.

From that, you’re supposed to check that every pixel in the file is similar to that RGB, and if it is, it creates zone and checks the neighborhood of that pixel. If similar, add to that zone.

I started doing this, but after that I don’t know where to go. In this case, I want to calculate blobs, ie, see if the pixels of the neighborhood of a certain pixel are similar, and thus create a zone.

int LerFicheiro(char *fi, IMAGEM *Imag){

    int i, j ,nC, nL,ncanais;
    char nome;
    FILE *f;
    PIXEL *p;
    PIXEL **m;
    p = (PIXEL *)malloc(sizeof(PIXEL));
    f = fopen(fi, "r");  // abre um ficheiro qualquer
    while (!feof(f))
    {
        fread(&nome, sizeof(IMAGEM), 1, f);  // nome da imagem
        fread(&Imag->NLINHAS, sizeof(IMAGEM), 1, f); //  nº linhas
        fread(&Imag->NCOLUNAS, sizeof(IMAGEM), 1, f);  // nº colunas
        fread(&Imag->NCANAIS, sizeof(IMAGEM), 1, f);  // nº canais
        m=criarmatriz(Imag->NLINHAS, Imag->NCOLUNAS);
        for (i = 0; i <= Imag->NLINHAS; i++)
        {
            for (j = 0; j < Imag->NCOLUNAS; j++)
            {
                fread(&m[i][j].R, sizeof(IMAGEM), 1, f);
                fread(&m[i][j].G, sizeof(IMAGEM), 1, f);
                fread(&m[i][j].B, sizeof(IMAGEM), 1, f);


            }
        }
    }
    fclose(f);
    return INSUCESSO;
}  
int calcularZonas(IMAGEM *Imag, int R, int G, int B, int D) {

    int i, j;
    PIXEL *p;
    BLOCO *b;
    PIXEL **m;
    b = (BLOCO *)malloc(sizeof(BLOCO));
    for (i = 0; i <= Imag->NLINHAS; i++)
    {
        for (j = 0; j < Imag->NCOLUNAS; j++)
        {
            if ((m[i][j].R>= R - D) &&( m[i][j].R <= R + D))        // vai comparar o R dado pela função com o pixel da imagem, guardado na matriz.
            {
                if ((m[i][j].G >= G - D) && (m[i][j].G <= G + D))
                {
                    if ((m[i][j].B >= B - D )&& (m[i][j].B <= B + D))
                    {

                    }
                }
            }

        }
    }
    return INSUCESSO;
}

    typedef struct {

        int R, G, B;
    } *ptPIXEL, PIXEL;

    typedef struct Elem {

        PIXEL *info;

        struct Elem *seg;

        struct Elem *ant;
    } *ptELEMENTO, Elemento;

    typedef struct {

        int NLINHAS, NCOLUNAS, NCANAIS;

        Elemento *inicio;

        int nEl;

    } *ptIMAGEM, IMAGEM;

    IMAGEM *criar_lista() {

        IMAGEM *L;

        L = (IMAGEM *) malloc(sizeof(IMAGEM));

        L->inicio = NULL;

        L->nEl = 0;

        return L;
  }

inserir a descrição da imagem aqui

  • Where is the code that defines what is BLOCO and what’s inside of criarmatriz? What is the number of image channels? This file format has been set by you or not?

  • 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)?

  • @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.

  • @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

  • 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.

  • 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?

  • @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.

  • 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.

  • 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.

  • 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.

Show 5 more comments

1 answer

1


Before those of yours structare 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 #includes:

#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 ints 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 ints 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;
}
  • Thank you very much!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.