/* * R T _ S P E C T _ M A K E _ N T S C _ R G B * * Using the "Representative set of camera taking sensitivities" * for a NTSC television camera, from Benson "Television Engineering * Handbook" page 4.58, convert an RGB value in range 0..1 to * a spectral curve also in range 0..1. * * These curves should be used in converting spectral samples * to NTSC RGB values. */ void spect_make_NTSC_RGB(struct bn_tabdata **rp, struct bn_tabdata **gp, struct bn_tabdata **bp, const struct bn_table *tabp) { BN_CK_TABLE(tabp); /* Convert array of number pairs into bn_tabdata & bn_table */ rt_NTSC_r_tabdata = bn_tabdata_from_array(&rt_NTSC_R[0][0]); rt_NTSC_g_tabdata = bn_tabdata_from_array(&rt_NTSC_G[0][0]); rt_NTSC_b_tabdata = bn_tabdata_from_array(&rt_NTSC_B[0][0]); bu_log("ntsc_R: area=%g\n", bn_tabdata_area2(rt_NTSC_r_tabdata)); bn_print_table_and_tabdata("/dev/tty", rt_NTSC_r_tabdata); bu_log("ntsc_G: area=%g\n", bn_tabdata_area2(rt_NTSC_g_tabdata)); bn_print_table_and_tabdata("/dev/tty", rt_NTSC_g_tabdata); bu_log("ntsc_B: area=%g\n", bn_tabdata_area2(rt_NTSC_b_tabdata)); bn_print_table_and_tabdata("/dev/tty", rt_NTSC_b_tabdata); /* Resample original NTSC curves to match given bn_table sampling */ #if 0 /* just to test the routine */ *rp = bn_tabdata_resample_avg(tabp, rt_NTSC_r_tabdata); *gp = bn_tabdata_resample_avg(tabp, rt_NTSC_g_tabdata); *bp = bn_tabdata_resample_avg(tabp, rt_NTSC_b_tabdata); #else /* use this one for real */ *rp = bn_tabdata_resample_max(tabp, rt_NTSC_r_tabdata); *gp = bn_tabdata_resample_max(tabp, rt_NTSC_g_tabdata); *bp = bn_tabdata_resample_max(tabp, rt_NTSC_b_tabdata); #endif }
/* * R T _ S P E C T _ C U R V E _ T O _ X Y Z * * Convenience routine. * Serves same function as Roy Hall's CLR_spect_to_xyz(), pg 233. * The normalization xyz_scale = 1.0 / bn_tabdata_area2(cie_y); * has been folded into spect_make_CIE_XYZ(); */ void spect_curve_to_xyz(point_t xyz, const struct bn_tabdata *tabp, const struct bn_tabdata *cie_x, const struct bn_tabdata *cie_y, const struct bn_tabdata *cie_z) { fastf_t tab_area; BN_CK_TABDATA(tabp); #if 0 tab_area = bn_tabdata_area2(tabp); bu_log(" tab_area = %g\n", tab_area); if (fabs(tab_area) < VDIVIDE_TOL) { bu_log("spect_curve_to_xyz(): Area = 0 (no luminance) in this part of the spectrum\n"); VSETALL(xyz, 0); return; } tab_area = 1 / tab_area; #else /* This is what Roy says to do, but I'm not certain */ tab_area = 1; #endif xyz[X] = bn_tabdata_mul_area2(tabp, cie_x) * tab_area; xyz[Y] = bn_tabdata_mul_area2(tabp, cie_y) * tab_area; xyz[Z] = bn_tabdata_mul_area2(tabp, cie_z) * tab_area; }
static int test_bn_tabdata_area2(int argc, char *argv[]) { struct bn_table *tab_in; struct bn_tabdata *td_in; fastf_t expected, actual; if (argc != 5) { bu_exit(1, "<args> format: table tabdata expected_result [%s]\n", argv[0]); } scan_tab_args(argv[2], &tab_in); scan_tabdata_args(argv[3], &td_in, tab_in); sscanf(argv[4], "%lg", &expected); actual = bn_tabdata_area2(td_in); bu_log("Result: %g\n", actual); return !NEAR_EQUAL(expected, actual, BN_TOL_DIST); }
/** * R T _ S P E C T _ M A K E _ C I E _ X Y Z * * Given as input a spectral sampling distribution, generate the 3 * curves to match the human eye's response in CIE color parameters X, * Y, and Z. XYZ space can be readily converted to RGB with a 3x3 * matrix. * * The tabulated data is linearly interpolated. * * Pointers to the three spectral weighting functions are "returned", * storage for the X, Y, and Z curves is allocated by this routine and * must be freed by the caller. */ void rt_spect_make_CIE_XYZ(struct bn_tabdata **x, struct bn_tabdata **y, struct bn_tabdata **z, const struct bn_table *tabp) { struct bn_tabdata *a, *b, *c; fastf_t xyz_scale; int i; int j; BN_CK_TABLE(tabp); i = bn_table_interval_num_samples( tabp, 430., 650. ); if ( i <= 4 ) bu_log("rt_spect_make_CIE_XYZ: insufficient samples (%d) in visible band\n", i); BN_GET_TABDATA( a, tabp ); BN_GET_TABDATA( b, tabp ); BN_GET_TABDATA( c, tabp ); *x = a; *y = b; *z = c; /* No CIE data below 380 nm */ for ( j=0; tabp->x[j] < 380 && j < tabp->nx; j++ ) { a->y[j] = b->y[j] = c->y[j] = 0; } /* Traverse the CIE table. Produce as many output values as * possible before advancing to next CIE table entry. */ for ( i = 0; i < 81-1; i++ ) { fastf_t fract; /* fraction from [i] to [i+1] */ again: if ( j >= tabp->nx ) break; if ( tabp->x[j] < rt_CIE_XYZ[i][0] ) bu_bomb("rt_spect_make_CIE_XYZ assertion1 failed\n"); if ( tabp->x[j] >= rt_CIE_XYZ[i+1][0] ) continue; /* The CIE table has 5nm spacing */ fract = (tabp->x[j] - rt_CIE_XYZ[i][0] ) / 5; if ( fract < 0 || fract > 1 ) bu_bomb("rt_spect_make_CIE_XYZ assertion2 failed\n"); a->y[j] = (1-fract) * rt_CIE_XYZ[i][1] + fract * rt_CIE_XYZ[i+1][1]; b->y[j] = (1-fract) * rt_CIE_XYZ[i][2] + fract * rt_CIE_XYZ[i+1][2]; c->y[j] = (1-fract) * rt_CIE_XYZ[i][3] + fract * rt_CIE_XYZ[i+1][3]; j++; goto again; } /* No CIE data above 780 nm */ for (; j < tabp->nx; j++ ) { a->y[j] = b->y[j] = c->y[j] = 0; } /* Normalize the curves so that area under Y curve is 1.0 */ xyz_scale = bn_tabdata_area2( b ); if ( fabs(xyz_scale) < VDIVIDE_TOL ) { bu_log("rt_spect_make_CIE_XYZ(): Area = 0 (no luminance) in this part of the spectrum, skipping normalization step\n"); return; } xyz_scale = 1 / xyz_scale; bn_tabdata_scale( a, a, xyz_scale ); bn_tabdata_scale( b, b, xyz_scale ); bn_tabdata_scale( c, c, xyz_scale ); }