/* Veja {ex_yuv.h}. */ /* Last edited on 2024-12-21 14:05:30 by stolfi */ #include #include #include #include #include /* BIBLIOTECAS DO PROFESSOR: */ #include /* Vetores do {R^3}. */ #include /* Matrizes {3×3}. */ #include /* Tipo {bool_t}, {TRUE}, {FALSE}. */ #include /* {open_read}, {open_write}. */ #include /* Imagens PBM/PGM/PPM. */ #include /* Imagens em formato float. */ #include /* Conversão de imagens. */ #include /* Conversão de imagens. */ #include /* Pega parâmetros da linha de comando. */ #include /* PARÂMETROS OBTIDOS DA LINHA DE COMANDO: */ typedef struct options_t { char *ent_name; /* Nome de entrada. */ char *sai_name; /* Nome de saída. */ double RY, GY, BY; /* Relative brightness of the three primaries. */ } options_t; /* PROTÓTIPOS ADICIONAIS: */ float_image_t *ex_gera_teste_de_gama(int nOver, int bWd, double Y); /* Gera uma imagem apropriada para testar se as imagens estão sendo gravadas com correção gama adequada. A imagem consiste de {2*nOver + 1) faixas de cinza sólido de brilho {Y}, sendo a faixa central de intensidade 0.5, alternada com faixas de zebras claro/escuro que dão brilho médio {Y}. */ float_image_t *ex_gera_teste_de_brilho(int nOver, int size, r3x3_t *RGB_to_YVA); /* Gera uma imagem apropriada para testar os coeficientes . da fórmula de brilho (primeira coluna da matriz {RGB_to_YVA}. A imagem é uma matriz de {3 × nOver} blocos. cada bloco tem {size × size} pixels e contém um padrão pintado com uma cor primária contra um fundo cinza de mesmo brilho, mais ou menos um teco. */ void ex_separa_canais(float_image_t *E, r3x3_t *M, float_image_t *Y, float_image_t *V, float_image_t *A); /* Separa os três canais ('brilho', 'vermez', 'amarez') da imagem {E} e coloca em {Y,V,A}. Usa a matriz {M} para mapear {R,G,B} (considerado como um vetor linha) em {Y,V,A} (idem). */ void ex_relativiza_cor(float_image_t *C, float_image_t *Y, float_image_t *R); /* Computes the ratio fo the chroma image {C} relative to the brightness image {Y}, and saves the result in {R}. */ void ex_borra_imagem(float_image_t *E, int n, float_image_t *Eb); /* Aplca um filtro suavizador na imagem {E} com núcleo de Hann de {2*n+1} pixels em cada eixo, e coloca o resultado em {Eb}. The images {E} and {Eb} must be disjoint. */ void ex_junta_canais(float_image_t *Y, float_image_t *V, float_image_t *A, r3x3_t *M, float_image_t *S); /* Junta {Y,V,A} supondo que são 'brilho', 'vermez', 'amarez', coloca resultado em {S}. Se {Y} é NULL, supõe uma imagem com todas as amostras valendo 0.5. Se {V} e/ou {A} for NULL, supõe uma imagem com todas as amostras nulas. Usa a matriz {M} para mapear {Y,V,A} (considerado como um vetor linha) em {R,G,B} (idem). */ void ex_mostra_croma(double Y, double a, float_image_t *Vr, float_image_t *Ar, r3x3_t *M, float_image_t *S); /* Junta uma imagem de brilho constante {Y} com as imagens {Vr,Ar}, multiplicadas por {a}, supondo que são 'vermez' e 'amarez' relativos a {Y}. Coloca resultado em {S}. Se {Vr} e/ou {Ar} for NULL, supõe uma imagem com todas as amostras nulas. Usa a matriz {M} para mapear {Y,V,A} (considerado como um vetor linha) em {R,G,B} (idem). */ void ex_clip_RGB_image(float_image_t *C, r3x3_t *M); /* Assumes that {C} is a three-channel image with RGB color channels. Clips every pixel independently to the unit RGB cube {[0_1]^3}. The brightness {Y} is clipped to the range {[0_1]}, the hue is preserved, and the saturation is reduced if needed. */ /* PROGRAMA PRINCIPAL: */ int main(int argc, char** argv) { double wr_gamma = WR_GAMMA_SUN; double wr_bias = BIAS_SUN; double rd_gamma = RD_GAMMA_PC; double rd_bias = BIAS_PC; /* Pega os parâmetros da linha de comando: */ options_t *o = ex_parse_options(argc, argv); /* Gera uma imagem para teste de gama do monitor: */ int ig; for (ig = 1; ig <= 3; ig++) { double sY = ((double)ig)/4.0; float_image_t *TG = ex_gera_teste_de_gama(2, 20, sY); char *fname = jsprintf("0-TEST-GAMMA-%04d", (int)(sY*1000)); ex_write_image(fname, ".pgm", 0.0, 1.0, wr_gamma, wr_bias, TG); float_image_free(TG); } /* Garante normalização da fórmula de brilho: */ double RY, GY, BY; { double T = o->RY + o->GY + o->BY; RY = o->RY/T; GY = o->GY/T; BY = o->BY/T; } /* Constroi as matrizes {RGB <-> YVA}: */ r3x3_t RGB_to_YVA = (r3x3_t){{ { RY, +0.50, +0.25}, { GY, -0.50, +0.25}, { BY, 00.00, -0.50} }}; r3x3_t YVA_to_RGB; r3x3_inv(&RGB_to_YVA, &YVA_to_RGB); /* Gera uma imagem para teste da fórmula de brilho: */ float_image_t *TY = ex_gera_teste_de_brilho(2, 100, &RGB_to_YVA); ex_write_image("0-TEST-LUM", ".ppm", 0.0, 1.0, wr_gamma, wr_bias, TY); float_image_free(TY); TY = NULL; return 0; /* Lê a imagem de entrada {E}: */ float_image_t *E = ex_read_image(o->ent_name, ".ppm", rd_gamma, rd_bias, 0.0, 1.0); /* Define o tamanho da imagem-resultado: */ demand(E->sz[0] == 3, "imagem deve ser colorida"); int NX = E->sz[1]; /* Número de colunas */ int NY = E->sz[2]; /* Número de linhas */ /* Cria as imagens com os canais {Y,V,A} : */ float_image_t *Y = float_image_new(1, NX, NY); float_image_t *V = float_image_new(1, NX, NY); float_image_t *A = float_image_new(1, NX, NY); ex_separa_canais(E, &RGB_to_YVA, Y, V, A); ex_write_image(o->sai_name, "-Y.pgm", 00.000, +1.000, wr_gamma, wr_bias, Y); ex_write_image(o->sai_name, "-V.pgm", -0.500, +0.500, wr_gamma, wr_bias, V); ex_write_image(o->sai_name, "-A.pgm", -0.500, +0.500, wr_gamma, wr_bias, A); /* Calcula e grava as imagens com {V,A} relativo: */ float_image_t *Vr = float_image_new(1, NX, NY); ex_relativiza_cor(V, Y, Vr); ex_write_image(o->sai_name, "-Vr.pgm", -2.000, +2.000, wr_gamma, wr_bias, Vr); float_image_t *Ar = float_image_new(1, NX, NY); ex_relativiza_cor(A, Y, Ar); ex_write_image(o->sai_name, "-Ar.pgm", -2.000, +2.000, wr_gamma, wr_bias, Ar); /* Borra as tres imagens : */ float_image_t *Yb = float_image_new(1, NX, NY); ex_borra_imagem(Y, 12, Yb); ex_write_image(o->sai_name, "-Yb.pgm", 00.000, +1.000, wr_gamma, wr_bias, Yb); float_image_t *Vb = float_image_new(1, NX, NY); ex_borra_imagem(V, 12, Vb); ex_write_image(o->sai_name, "-Vb.pgm", -0.500, +0.500, wr_gamma, wr_bias, Vb); float_image_t *Ab = float_image_new(1, NX, NY); ex_borra_imagem(A, 12, Ab); ex_write_image(o->sai_name, "-Ab.pgm", -0.500, +0.500, wr_gamma, wr_bias, Ab); /* Recombina as três imagens sem borrar: */ float_image_t *YVA = float_image_new(3, NX, NY); ex_junta_canais(Y, V, A, &YVA_to_RGB, YVA); ex_clip_RGB_image(YVA, &RGB_to_YVA); ex_write_image(o->sai_name, "-YVA.ppm", 00.000, +1.000, wr_gamma, wr_bias, YVA); /* Recombina brilho e vermez sem borrar: */ float_image_t *YV = float_image_new(3, NX, NY); ex_junta_canais(Y, V, NULL, &YVA_to_RGB, YV); ex_clip_RGB_image(YV, &RGB_to_YVA); ex_write_image(o->sai_name, "-YV.ppm", 00.000, +1.000, wr_gamma, wr_bias, YV); /* Recombina brilho e amarez sem borrar: */ float_image_t *YA = float_image_new(3, NX, NY); ex_junta_canais(Y, NULL, A, &YVA_to_RGB, YA); ex_clip_RGB_image(YA, &RGB_to_YVA); ex_write_image(o->sai_name, "-YA.ppm", 00.000, +1.000, wr_gamma, wr_bias, YA); /* Recombina vermez e amarez com brilho 0.5 sem borrar: */ float_image_t *VA = float_image_new(3, NX, NY); ex_mostra_croma(0.3, 0.5, Vr, Ar, &YVA_to_RGB, VA); ex_clip_RGB_image(VA, &RGB_to_YVA); ex_write_image(o->sai_name, "-VA.ppm", 00.000, +1.000, wr_gamma, wr_bias, VA); /* Recombina as três imagens com croma trocado, sem borrar: */ float_image_t *YAV = float_image_new(3, NX, NY); ex_junta_canais(Y, A, V, &YVA_to_RGB, YAV); ex_write_image(o->sai_name, "-YAV.ppm", 00.000, +1.000, wr_gamma, wr_bias, YAV); /* Recombina as imagens com croma borrado: */ float_image_t *YVbAb = float_image_new(3, NX, NY); ex_junta_canais(Y, Vb, Ab, &YVA_to_RGB, YVbAb); ex_clip_RGB_image(YVbAb, &RGB_to_YVA); ex_write_image(o->sai_name, "-YVbAb.ppm", 00.000, +1.000, wr_gamma, wr_bias, YVbAb); /* Recombina as imagens com brilho borrado: */ float_image_t *YbVA = float_image_new(3, NX, NY); ex_junta_canais(Yb, V, A, &YVA_to_RGB, YbVA); ex_clip_RGB_image(YbVA, &RGB_to_YVA); ex_write_image(o->sai_name, "-YbVA.ppm", 00.000, +1.000, wr_gamma, wr_bias, YbVA); return 0; } /* PROCESSAMENTO DAS IMAGENS: */ void ex_separa_canais(float_image_t *E, r3x3_t *M, float_image_t *Y, float_image_t *V, float_image_t *A) { demand(E->sz[0] == 3, "imagem de entrada {E} deve ser colorida"); demand(Y->sz[0] == 1, "imagem de saída {Y} deve ser cinérea"); demand(V->sz[0] == 1, "imagem de saída {V} deve ser cinérea"); demand(A->sz[0] == 1, "imagem de saída {A} deve ser cinérea"); int NX = E->sz[1]; int NY = E->sz[2]; int ix, iy; for (ix = 0; ix < NX; ix++) for (iy = 0; iy < NY; iy++) { float sR = float_image_get_sample(E, 0, ix, iy); float sG = float_image_get_sample(E, 1, ix, iy); float sB = float_image_get_sample(E, 2, ix, iy); r3_t sRGB = (r3_t){{ sR, sG, sB }}; /* Convert to brightness and chroma: */ r3_t sYVA; r3x3_map_row(&sRGB, M, &sYVA); float_image_set_sample(Y, 0, ix, iy, sYVA.c[0]); float_image_set_sample(V, 0, ix, iy, sYVA.c[1]); float_image_set_sample(A, 0, ix, iy, sYVA.c[1]); } } void ex_relativiza_cor(float_image_t *C, float_image_t *Y, float_image_t *R) { demand(C->sz[0] == 1, "imagem de entrada {C} deve ser cinérea"); demand(Y->sz[0] == 1, "imagem de entrada {Y} deve ser cinérea"); demand(R->sz[0] == 1, "imagem de saída {R} deve ser cinérea"); int NX = C->sz[1]; int NY = C->sz[2]; int ix, iy; for (ix = 0; ix < NX; ix++) for (iy = 0; iy < NY; iy++) { float sC = float_image_get_sample(C, 0, ix, iy); float sY = float_image_get_sample(Y, 0, ix, iy); /* Convert chroma to relative chroma: */ double f = 1.0/hypot(sY, 0.001); sC = f*sC; float_image_set_sample(R, 0, ix, iy, sC); } } void ex_junta_canais(float_image_t *Y, float_image_t *V, float_image_t *A, r3x3_t *M, float_image_t *S) { if (Y != NULL) demand(Y->sz[0] == 1, "imagem de {Y} entrada deve ser cinérea"); if (V != NULL) demand(V->sz[0] == 1, "imagem de {V} entrada deve ser cinérea"); if (A != NULL) demand(A->sz[0] == 1, "imagem de {A} entrada deve ser cinérea"); demand(S->sz[0] == 3, "imagem de saída {S} deve ser colorida"); int NX = S->sz[1]; int NY = S->sz[2]; int ix, iy; for (ix = 0; ix < NX; ix++) for (iy = 0; iy < NY; iy++) { float sY = (Y == NULL ? 0.5 : float_image_get_sample(Y, 0, ix, iy)); float sV = (V == NULL ? 0.0 : float_image_get_sample(V, 0, ix, iy)); float sA = (A == NULL ? 0.0 : float_image_get_sample(A, 0, ix, iy)); r3_t sYVA = (r3_t){{ sY, sV, sA }}; /* Convert to RGB: */ r3_t sRGB; r3x3_map_row(&sYVA, M, &sRGB); float_image_set_sample(S, 0, ix, iy, sRGB.c[0]); float_image_set_sample(S, 1, ix, iy, sRGB.c[1]); float_image_set_sample(S, 2, ix, iy, sRGB.c[2]); } } void ex_mostra_croma(double Y, double a, float_image_t *Vr, float_image_t *Ar, r3x3_t *M, float_image_t *S) { if (Vr != NULL) demand(Vr->sz[0] == 1, "imagem de {Vr} entrada deve ser cinérea"); if (Ar != NULL) demand(Ar->sz[0] == 1, "imagem de {Ar} entrada deve ser cinérea"); demand(S->sz[0] == 3, "imagem de saída {S} deve ser colorida"); /* Chroma scaling factor: */ double f = a * hypot(Y, 0.001); int NX = S->sz[1]; int NY = S->sz[2]; int ix, iy; for (ix = 0; ix < NX; ix++) for (iy = 0; iy < NY; iy++) { float sY = Y; float rV = (Vr == NULL ? 0.0 : float_image_get_sample(Vr, 0, ix, iy)); float rA = (Ar == NULL ? 0.0 : float_image_get_sample(Ar, 0, ix, iy)); /* Convert relative chroma to chroma: */ double sV = f*rV; double sA = f*rA; r3_t sYVA = (r3_t){{ sY, sV, sA }}; /* Convert to RGB: */ r3_t sRGB; r3x3_map_row(&sYVA, M, &sRGB); float_image_set_sample(S, 0, ix, iy, sRGB.c[0]); float_image_set_sample(S, 1, ix, iy, sRGB.c[1]); float_image_set_sample(S, 2, ix, iy, sRGB.c[2]); } } void ex_borra_imagem(float_image_t *E, int n, float_image_t *Eb) { int NC = E->sz[0]; /* Número de canais */ int NX = E->sz[1]; /* Número de colunas */ int NY = E->sz[2]; /* Número de linhas */ int ic, ix, iy, r; double peso[n+1]; for (r = 0; r <= n; r++) { peso[r] = 0.5*(1.0 + cos(M_PI*(r + 0.5)/n)); } for (ic = 0; ic < NC; ic++) for (ix = 0; ix < NX; ix++) for (iy = 0; iy < NY; iy++) { double sum_pv = 0; double sum_p = 0; int kx, ky; for (kx = ix-n; kx <= ix+n; kx++) for (ky = iy-n; ky <= iy+n; ky++) { if ((kx >= 0) && (kx < NX) && (ky >= 0) && (ky < NY)) { double p = peso[abs(kx-ix)]*peso[abs(ky-iy)]; double v = float_image_get_sample(E, ic, kx, ky); sum_pv += p*v; sum_p += p; } } double v = sum_pv/sum_p; float_image_set_sample(Eb, ic, ix, iy, v); } } float_image_t *ex_gera_teste_de_gama(int nOver, int bWd, double Y) { int nBands = 3 + 4*nOver; /* Total number of vertical bands. */ int NX = nBands*bWd; /* Image width in pixels. */ int NY = 2*NX + 1; /* Image height in pixels. */ double loY = (Y <= 0.5 ? 0.0 : 2*Y - 1.0); /* Y of dark stripes. */ double hiY = (Y <= 0.5 ? 2*Y : 1.0); /* Y of dark stripes. */ float_image_t *A = float_image_new(1, NX, NY); /* Fill even bands with alternating {loY}/{hiY} lines: */ int ib, ix, iy; for (iy = 0; iy < NY; iy++) { float v = (iy % 2 == 0 ? loY : hiY); for (ib = 0; ib < nBands; ib += 2) for (ix = ib*bWd; ix < (ib+1)*bWd; ix++) { float_image_set_sample(A, 0, ix, iy, v); } } /* Fill odd bands with solid {Y} grey, with variations from center: */ for (ib = 1; ib < nBands; ib += 2) { float v = Y*powf(0.95, (ib-1)/2 - nOver); for (ix = ib*bWd; ix < (ib+1)*bWd; ix++) for (iy = 0; iy < NY; iy++) { float_image_set_sample(A, 0, ix, iy, v); } } return A; } float_image_t *ex_gera_teste_de_brilho(int nOver, int size, r3x3_t *RGB_to_YVA) { int NC = 3; /* Num channels. */ int NF = 2*nOver+1; /* Num of block sper row. */ int NX = NF*size; /* Image width in pixels. */ int NY = NC*size; /* Image height in pixels. */ double R = 0.5; /* Relative radius of pattern. */ double w = 2.0/size; /* Relative line width. */ double r9o = R - 0.0*w, r9i = r9o - w; /* Radius of outer square */ double r2o = R - 1.5*w, r2i = r2o - w; /* Radius of outer square */ double r1o = R - 3.0*w, r1i = r1o - w; /* Radius of outer square */ float_image_t *A = float_image_new(NC, NX, NY); /* Scan the pixels in a block: */ int ix, iy, kc, kf, ic; for (ix = 0; ix < size; ix++) { for (iy = 0; iy < size; iy++) { /* Decide whether pixel {ix,iy} is in pattern or outside it: */ double dx = (2*ix - size)/((double)size); double dy = (2*iy - size)/((double)size); double r9 = fmax(fabs(dx), fabs(dy)); double r2 = hypot(dx,dy); double r1 = fabs(dx) + fabs(dy); bool_t inside1 = (r1 <= r1o) && (r1 > r1i); bool_t inside2 = (r2 <= r2o) && (r2 > r2i); bool_t inside9 = (r9 <= r9o) && (r9 > r9i); bool_t inside = inside1 | inside2 | inside9; /* Set the pixel {ix,iy} in every block: */ for (kf = 0; kf < NF; kf++) { double f = pow(1.05, kf - nOver); /* Fator de clareamento. */ int kx = kf*size; /* {x} offset of block. */ for (kc = 0; kc < NC; kc++) { /* Paint pixel in block of channel {kc}, factor {f}: */ int ky = kc*size; /* {y} offset of block. */ double sY = RGB_to_YVA->c[kc][0]; /* Nominal {Y} of primary {kc}. */ /* Fill block: */ for (ic = 0; ic < NC; ic++) { double v = (inside ? (ic == kc ? 1 : 0) : f*sY); float_image_set_sample(A, ic, kx + ix, ky + iy, v); } } } } } return A; } /* LEITURA E GRAVACÃO DA IMAGEM */ void ex_clip_RGB_image(float_image_t *C, r3x3_t *M) { demand(C->sz[0] == 3, "imagem {C} deve ser colorida"); int NX = C->sz[1]; int NY = C->sz[2]; int nClip = 0; /* Number of clipped pixels. */ double aMin = 1.0; int ix, iy; for (ix = 0; ix < NX; ix++) for (iy = 0; iy < NY; iy++) { float *pR = float_image_get_sample_address(C, 0, ix, iy); float *pG = float_image_get_sample_address(C, 1, ix, iy); float *pB = float_image_get_sample_address(C, 2, ix, iy); /* Convert to brightness and chroma: */ r3_t sRGB = (r3_t){{ (*pR), (*pG), (*pB) }}; r3_t sYVA; r3x3_map_row(&sRGB, M, &sYVA); /* Clip the brightness {sY} to {[0 _ 1]}: */ double sY = sYVA.c[0]; if (sY < 0) { sY = 0; } if (sY > 1) { sY = 1; } /* Compute the luma and components {gRGB, cRGB} in RGB coordinates: */ double cR = (*pR) - sY; double cG = (*pG) - sY; double cB = (*pB) - sY; /* Find the max {a} in {[0_1]} such that {gRGB + a*cRGB} is inside the unit cube: */ double a = 1.0; if (cR < 0) { a = fmin(a, -sY/cR); } else if (cR > 0) { a = fmin(a, (1-sY)/cR); } if (cG < 0) { a = fmin(a, -sY/cG); } else if (cG > 0) { a = fmin(a, (1-sY)/cG); } if (cB < 0) { a = fmin(a, -sY/cB); } else if (cB > 0) { a = fmin(a, (1-sY)/cB); } if ((a < 1) || (sY != sYVA.c[0])) { nClip++; } if (a < aMin) { aMin = a; } /* Compute the final color as {gRGB + a*cRGB}: */ (*pR) = sY + a*cR; (*pG) = sY + a*cG; (*pB) = sY + a*cB; } fprintf(stderr, " clipped %d float pixels to the RGB cube (aMin = %6.4f)\n", nClip, aMin); } float_image_t *ex_read_image(char *name, char *tag, double gamma, double bias, double vMin, double vMax) { /* Assemble file name: */ char *fname = jsprintf("%s%s", name, tag); fprintf(stderr, "\n"); fprintf(stderr, "reading float image from file %s...\n", fname); /* Open file: */ FILE *rd = open_read(fname, TRUE); /* Read PNM image: */ uint16_image_t *pim = uint16_image_read_pnm_file(rd); fclose(rd); free(fname); /* Convert image to floats: */ float_image_t *A = float_image_from_uint16_image(pim, NULL, NULL, TRUE, TRUE); /* Apply gamma-correction and affine scaling to image {A}: */ int NC = A->sz[0]; int ic; for (ic = 0; ic < NC; ic++) { float_image_apply_gamma(A, ic, gamma, bias); float_image_rescale_samples(A, ic, 0.0, 1.0, vMin, vMax); } uint16_image_free(pim); return A; } void ex_write_image(char *name, char *tag, double vMin, double vMax, double gamma, double bias, float_image_t *A) { /* Assemble file name: */ char *fname = jsprintf("%s%s", name, tag); fprintf(stderr, "\n"); fprintf(stderr, "writing float image to file %s...\n", fname); /* Create a copy of image {A}: */ float_image_t *B = float_image_copy(A); /* Apply scaling and gamma-correction to image {B}: */ int NC = A->sz[0]; int ic; for (ic = 0; ic < NC; ic++) { float_image_rescale_samples(B, ic, vMin, vMax, 0.0, 1.0); float_image_apply_gamma(B, ic, gamma, bias); } /* Convert image to integers: */ uint16_image_t *pim = float_image_to_uint16_image(B, NC, NULL, NULL, NULL, 255, TRUE, TRUE); float_image_free(B); /* Open file: */ FILE *wr = open_write(fname, TRUE); /* Write PNM image: */ bool_t forceplain = FALSE; bool_t verbose = TRUE; uint16_image_write_pnm_file(wr, pim, forceplain, verbose); fclose(wr); free(fname); uint16_image_free(pim); } /* ANÁLISE DA LINHA DE COMANDO: */ options_t *ex_parse_options(int argc, char **argv) { /* INICIALIZA A ANÁLISE: */ /* Cria e incializa o analisador de linha de comando {pp}: */ argparser_t *pp = argparser_new(stderr, argc, argv); argparser_set_help(pp, PROG_NAME " version " PROG_VERS ", usage:\n" PROG_HELP); argparser_set_info(pp, PROG_INFO); /* Processa as opções "-help" e "-info": */ argparser_process_help_info_options(pp); /* Aloca o registro {o} onde os parâmetros serão guardados: */ options_t *o = (options_t *)malloc(sizeof(options_t)); /* PEGA PARÂMETROS COM PALAVRA-CHAVE: */ if (argparser_keyword_present(pp, "-lum")) { o->RY = argparser_get_next_double(pp, 0.0, 1.0); o->GY = argparser_get_next_double(pp, 0.0, 1.0); o->BY = argparser_get_next_double(pp, 0.0, 1.0); } else { o->RY = 0.299; o->GY = 0.587; o->BY = 0.114; } /* PEGA PARÂMETROS POSICIONAIS: */ /* Pule para o primeiro parâmetro posicional: */ argparser_skip_parsed(pp); /* Pega o nome da imagem {A}: */ o->ent_name = argparser_get_next(pp); o->sai_name = argparser_get_next(pp); /* FINALIZA A ANÁLISE: */ /* Verifica se há argumentos sobrando: */ argparser_finish(pp); return o; }