def _compute_groups(samples, test_type, grouping, subjects, paired, *args): """ calculate test statistic for PERMDISP""" groups = [] samples['grouping'] = grouping if test_type == 'centroid': centroids = samples.groupby('grouping').aggregate('mean') elif test_type == 'median': centroids = samples.groupby('grouping').aggregate(_config_med) for label, df in samples.groupby('grouping'): groups.append( cdist(df.values[:, :-1], [centroids.loc[label].values], metric='euclidean')) stat, _ = f_oneway(*groups) stat = stat[0] # effect sizes: num_groups = len(np.unique(grouping)) sample_size = len(grouping) if paired == True: dfErr = (num_groups - 1) * (len(np.unique(subjects)) - 1) else: dfErr = sample_size - num_groups R2 = 1.0 - 1 / (1 + stat * num_groups / (dfErr - 1)) R2adj = 1 - ((1 - R2) * (sample_size - 1) / (sample_size - num_groups - 1)) effect_sizes = { 'p_eta2': np.nan, 'omega2': np.nan, 'R2': R2, 'R2adj': R2adj } # not yet determined return stat, effect_sizes
def _create_subjects_index_arr(subjects=None, grouping=None): """ Create subjects indexing array""" if subjects is None: if grouping is None: raise Exception('Grouping must be supplied!') groups = np.unique(grouping) for i, group in enumerate(groups): if i == 0: subjects = np.arange(((grouping == group) * 1).sum()) else: subjects = np.hstack( (subjects, np.arange(((grouping == group) * 1).sum()))) return subjects
def _permutate_grouping(grouping, subjects, paired=False): """ permutate grouping and subjects indexing arrays""" if paired == False: perm_idx = np.arange(grouping.shape[0], dtype=int) perm_idx = np.random.permutation(perm_idx) perm_grouping = grouping[perm_idx] perm_subjects = subjects[perm_idx] else: groups = np.unique(grouping) o = 10**((grouping.reshape(len(groups), len(grouping) // len(groups))) + 1) if subjects is not None: s = subjects.reshape(len(groups), len(grouping) // len(groups)) for i in range(o.shape[-1]): perm_idx = np.arange(o.shape[0], dtype=int) perm_idx = np.random.permutation(perm_idx) o[:, i] = o[perm_idx, i] s[:, i] = s[perm_idx, i] o = np.log10(o) - 1 perm_grouping = o.flatten() if subjects is not None: perm_subjects = s.flatten() return perm_grouping, perm_subjects
def render_image(img = None, spd = None, rfl = None, out = 'img_hyp', \ refspd = None, D = None, cieobs = _CIEOBS, \ cspace = 'xyz', cspace_tf = {}, CSF = None,\ interp_type = 'nd', k_neighbours = 4, show = True, verbosity = 0, show_ref_img = True,\ stack_test_ref = 12,\ write_to_file = None): """ Render image under specified light source spd. Args: :img: | None or str or ndarray with float (max = 1) rgb image. | None load a default image. :spd: | ndarray, optional | Light source spectrum for rendering | If None: use CIE illuminant F4 :rfl: | ndarray, optional | Reflectance set for color coordinate to rfl mapping. :out: | 'img_hyp' or str, optional | (other option: 'img_ren': rendered image under :spd:) :refspd: | None, optional | Reference spectrum for color coordinate to rfl mapping. | None defaults to D65 (srgb has a D65 white point) :D: | None, optional | Degree of (von Kries) adaptation from spd to refspd. :cieobs: | _CIEOBS, optional | CMF set for calculation of xyz from spectral data. :cspace: | 'xyz', optional | Color space for color coordinate to rfl mapping. | Tip: Use linear space (e.g. 'xyz', 'Yuv',...) for (interp_type == 'nd'), | and perceptually uniform space (e.g. 'ipt') for (interp_type == 'nearest') :cspace_tf: | {}, optional | Dict with parameters for xyz_to_cspace and cspace_to_xyz transform. :CSF: | None, optional | RGB camera response functions. | If None: input :xyz: contains raw rgb values. Override :cspace: | argument and perform estimation directly in raw rgb space!!! :interp_type: | 'nd', optional | Options: | - 'nd': perform n-dimensional linear interpolation using Delaunay triangulation. | - 'nearest': perform nearest neighbour interpolation. :k_neighbours: | 4 or int, optional | Number of nearest neighbours for reflectance spectrum interpolation. | Neighbours are found using scipy.spatial.cKDTree :show: | True, optional | Show images. :verbosity: | 0, optional | If > 0: make a plot of the color coordinates of original and rendered image pixels. :show_ref_img: | True, optional | True: shows rendered image under reference spd. False: shows | original image. :write_to_file: | None, optional | None: do nothing, else: write to filename(+path) in :write_to_file: :stack_test_ref: | 12, optional | - 12: left (test), right (ref) format for show and imwrite | - 21: top (test), bottom (ref) | - 1: only show/write test | - 2: only show/write ref | - 0: show both, write test Returns: :returns: | img_hyp, img_ren, | ndarrays with float hyperspectral image and rendered images """ # Get image: #imread = lambda x: plt.imread(x) #matplotlib.pyplot if img is not None: if isinstance(img, str): img = plt.imread(img) # use matplotlib.pyplot's imread else: img = plt.imread(_HYPSPCIM_DEFAULT_IMAGE) if isinstance(img, np.uint8): img = img / 255 elif isinstance(img, np.uint16): img = img / (2**16 - 1) # Convert to 2D format: rgb = img.reshape(img.shape[0] * img.shape[1], 3) # *1.0: make float rgb[rgb == 0] = _EPS # avoid division by zero for pure blacks. # Get unique rgb values and positions: rgb_u, rgb_indices = np.unique(rgb, return_inverse=True, axis=0) # get rfl set: if rfl is None: # use IESTM30['4880'] set rfl = _CRI_RFL['ies-tm30']['4880']['5nm'] wlr = rfl[ 0] # spectral reflectance set determines wavelength range for estimation (xyz_to_rfl()) # get Ref spd: if refspd is None: refspd = _CIE_ILLUMINANTS['D65'].copy() refspd = cie_interp( refspd, wlr, kind='linear') # force spd to same wavelength range as rfl # Convert rgb_u to xyz and lab-type values under assumed refspd: if CSF is None: xyz_wr = spd_to_xyz(refspd, cieobs=cieobs, relative=True) xyz_ur = colortf(rgb_u * 255, tf='srgb>xyz') else: xyz_ur = rgb_u # for input in xyz_to_rfl (when CSF is not None: this functions assumes input is indeed rgb !!!) # Estimate rfl's for xyz_ur: rfl_est, xyzri = xyz_to_rfl(xyz_ur, rfl = rfl, out = 'rfl_est,xyz_est', \ refspd = refspd, D = D, cieobs = cieobs, \ cspace = cspace, cspace_tf = cspace_tf, CSF = CSF,\ interp_type = interp_type, k_neighbours = k_neighbours, verbosity = verbosity) # Get default test spd if none supplied: if spd is None: spd = _CIE_ILLUMINANTS['F4'] if CSF is None: # calculate xyz values under test spd: xyzti, xyztw = spd_to_xyz(spd, rfl=rfl_est, cieobs=cieobs, out=2) # Chromatic adaptation from test spd to refspd: if D is not None: xyzti = cat.apply(xyzti, xyzw1=xyztw, xyzw2=xyz_wr, D=D) # Convert xyzti under test spd to srgb: rgbti = colortf(xyzti, tf='srgb') / 255 else: # Calculate rgb coordinates from camera sensitivity functions under spd: rgbti = rfl_to_rgb(rfl_est, spd=spd, CSF=CSF, wl=None) # Chromatic adaptation from test spd to refspd: if D is not None: white = np.ones_like(spd) white[0] = spd[0] rgbwr = rfl_to_rgb(white, spd=refspd, CSF=CSF, wl=None) rgbwt = rfl_to_rgb(white, spd=spd, CSF=CSF, wl=None) rgbti = cat.apply_vonkries2(rgbti, rgbwt, rgbwr, xyzw0=np.array([[1.0, 1.0, 1.0]]), in_='rgb', out_='rgb', D=1) # Reconstruct original locations for rendered image rgbs: img_ren = rgbti[rgb_indices] img_ren.shape = img.shape # reshape back to 3D size of original img_ren = img_ren # For output: if show_ref_img == True: rgb_ref = colortf(xyzri, tf='srgb') / 255 if ( CSF is None ) else xyzri # if CSF not None: xyzri contains rgbri !!! img_ref = rgb_ref[rgb_indices] img_ref.shape = img.shape # reshape back to 3D size of original img_str = 'Rendered (under ref. spd)' img = img_ref else: img_str = 'Original' img = img if (stack_test_ref > 0) | show == True: if stack_test_ref == 21: img_original_rendered = np.vstack( (img_ren, np.ones((4, img.shape[1], 3)), img)) img_original_rendered_str = 'Rendered (under test spd)\n ' + img_str elif stack_test_ref == 12: img_original_rendered = np.hstack( (img_ren, np.ones((img.shape[0], 4, 3)), img)) img_original_rendered_str = 'Rendered (under test spd) | ' + img_str elif stack_test_ref == 1: img_original_rendered = img_ren img_original_rendered_str = 'Rendered (under test spd)' elif stack_test_ref == 2: img_original_rendered = img img_original_rendered_str = img_str elif stack_test_ref == 0: img_original_rendered = img_ren img_original_rendered_str = 'Rendered (under test spd)' if write_to_file is not None: # Convert from RGB to BGR formatand write: #print('Writing rendering results to image file: {}'.format(write_to_file)) with warnings.catch_warnings(): warnings.simplefilter("ignore") imsave(write_to_file, img_original_rendered) if show == True: # show images using pyplot.show(): plt.figure() plt.imshow(img_original_rendered) plt.title(img_original_rendered_str) plt.gca().get_xaxis().set_ticklabels([]) plt.gca().get_yaxis().set_ticklabels([]) if stack_test_ref == 0: plt.figure() plt.imshow(img) plt.title(img_str) plt.axis('off') if 'img_hyp' in out.split(','): # Create hyper_spectral image: rfl_image_2D = rfl_est[ rgb_indices + 1, :] # create array with all rfls required for each pixel img_hyp = rfl_image_2D.reshape(img.shape[0], img.shape[1], rfl_image_2D.shape[1]) # Setup output: if out == 'img_hyp': return img_hyp elif out == 'img_ren': return img_ren else: return eval(out)
def genMonteCarloObs(n_obs=1, fieldsize=10, list_Age=[32], out='LMS', wl=None, allow_negative_values=False): """ Monte-Carlo generation of individual observer cone fundamentals. Args: :n_obs: | 1, optional | Number of observer CMFs to generate. :list_Age: | list of observer ages or str, optional | Defaults to 32 (cfr. CIE2006 CMFs) | If 'us_census': use US population census of 2010 to generate list_Age. :fieldsize: | fieldsize in degrees (between 2° and 10°), optional | Defaults to 10°. :out: | 'LMS' or str, optional | Determines output. :wl: | None, optional | Interpolation/extraplation of :LMS: output to specified wavelengths. | None: output original _WL = np.array([390,780,5]) :allow_negative_values: | False, optional | Cone fundamentals or color matching functions | should not have negative values. | If False: X[X<0] = 0. Returns: :returns: | LMS [,var_age, vAll] | - LMS: ndarray with population LMS functions. | - var_age: ndarray with population observer ages. | - vAll: dict with population physiological factors (see .keys()) References: 1. `Asano Y, Fairchild MD, and Blondé L (2016). Individual Colorimetric Observer Model. PLoS One 11, 1–19. <http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0145671>`_ 2. `Asano Y, Fairchild MD, Blondé L, and Morvan P (2016). Color matching experiment for highlighting interobserver variability. Color Res. Appl. 41, 530–539. <https://onlinelibrary.wiley.com/doi/abs/10.1002/col.21975>`_ 3. `CIE, and CIE (2006). Fundamental Chromaticity Diagram with Physiological Axes - Part I (Vienna: CIE). <http://www.cie.co.at/publications/fundamental-chromaticity-diagram-physiological-axes-part-1>`_ 4. `Asano's Individual Colorimetric Observer Model <https://www.rit.edu/cos/colorscience/re_AsanoObserverFunctions.php>`_ """ # Scale down StdDev by scalars optimized using Asano's 75 observers # collected in Germany: stdDevAllParam = _INDVCMF_STD_DEV_ALL_PARAM.copy() scale_factors = [0.98, 0.98, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] scale_factors = dict(zip(list(stdDevAllParam.keys()), scale_factors)) stdDevAllParam = { k: v * scale_factors[k] for (k, v) in stdDevAllParam.items() } # Get Normally-distributed Physiological Factors: vAll = getMonteCarloParam(n_obs=n_obs) if list_Age is 'us_census': list_Age = getUSCensusAgeDist() # Generate Random Ages with the same probability density distribution # as color matching experiment: sz_interval = 1 list_AgeRound = np.round(np.array(list_Age) / sz_interval) * sz_interval h = math.histogram(list_AgeRound, bins=np.unique(list_AgeRound), bin_center=True)[0] p = h / h.sum() # probability density distribution var_age = np.random.choice(np.unique(list_AgeRound), \ size = n_obs, replace = True,\ p = p) # Set requested wavelength range: if wl is not None: wl = getwlr(wl3=wl) else: wl = _WL LMS_All = np.zeros((3 + 1, wl.shape[0], n_obs)) LMS_All.fill(np.nan) for k in range(n_obs): t_LMS, t_trans_lens, t_trans_macula, t_sens_photopig = cie2006cmfsEx(age = var_age[k], fieldsize = fieldsize, wl = wl,\ var_od_lens = vAll['od_lens'][k], var_od_macula = vAll['od_macula'][k], \ var_od_L = vAll['od_L'][k], var_od_M = vAll['od_M'][k], var_od_S = vAll['od_S'][k],\ var_shft_L = vAll['shft_L'][k], var_shft_M = vAll['shft_M'][k], var_shft_S = vAll['shft_S'][k],\ out = 'LMS,trans_lens,trans_macula,sens_photopig') LMS_All[:, :, k] = t_LMS # listout = out.split(',') # if ('trans_lens' in listout) | ('trans_macula' in listout) | ('trans_photopig' in listout): # trans_lens[:,k] = t_trans_lens # trans_macula[:,k] = t_trans_macula # sens_photopig[:,:,k] = t_sens_photopig if n_obs == 1: LMS_All = np.squeeze(LMS_All, axis=2) if ('xyz' in out.lower().split(',')): LMS_All = lmsb_to_xyzb(LMS_All, fieldsize, out='xyz', allow_negative_values=allow_negative_values) out = out.replace('xyz', 'LMS').replace('XYZ', 'LMS') if ('lms' in out.lower().split(',')): out = out.replace('lms', 'LMS') if (out == 'LMS'): return LMS_All elif (out == 'LMS,var_age,vAll'): return LMS_All, var_age, vAll else: return eval(out)
def initialize_VF_hue_angles(hx = None, Cxr = _VF_MAXR, \ cri_type = _VF_CRI_DEFAULT, \ modeltype = _VF_MODEL_TYPE,\ determine_hue_angles = _DETERMINE_HUE_ANGLES): """ Initialize the hue angles that will be used to 'summarize' the VF model fitting parameters. Args: :hx: | None or ndarray, optional | None defaults to Munsell H5 hues. :Cxr: | _VF_MAXR, optional :cri_type: | _VF_CRI_DEFAULT or str or dict, optional, | Cri_type parameters for cri and VF model. :modeltype: | _VF_MODEL_TYPE or 'M5' or 'M6', optional | Determines the type of polynomial model. :determine_hue_angles: | _DETERMINE_HUE_ANGLES or True or False, optional | True: determines the 10 primary / secondary Munsell hues ('5..'). | Note that for 'M6', an additional Returns: :pcolorshift: | {'href': href, | 'Cref' : _VF_MAXR, | 'sig' : _VF_SIG, | 'labels' : list[str]} """ ########################################### # Get Munsell H5 hues: ########################################### rflM = _MUNSELL['R'] hn = _MUNSELL['H'] # all Munsell hues rH5 = np.where([ _MUNSELL['H'][:, 0][x][0] == '5' for x in range(_MUNSELL['H'][:, 0].shape[0]) ])[0] #all Munsell H5 hues hns5 = np.unique(_MUNSELL['H'][rH5]) #------------------------------------------------------------------------------ # Determine Munsell hue angles in cam02ucs: pool = False IllC = _CIE_ILLUMINANTS[ 'C'] # for determining Munsell hue angles in cam02ucs outM = VF_colorshift_model(IllC, cri_type=cri_type, sampleset=rflM, vfcolor='g', pool=pool) #------------------------------------------------------------------------------ if (determine_hue_angles == True) | (hx is None): # find samples at major Munsell hue angles: all_h5_Munsell_cam02ucs = np.ones(hns5.shape) Jabt_IllC = outM[0]['Jab']['Jabt'] for i, v in enumerate(hns5): hm = np.where(hn == v)[0] all_h5_Munsell_cam02ucs[i] = math.positive_arctan( [Jabt_IllC[hm, 0, 1].mean()], [Jabt_IllC[hm, 0, 2].mean()], htype='rad')[0] hx = all_h5_Munsell_cam02ucs #------------------------------------------------------------------------------ # Setp color shift parameters: pcolorshift = {'href': hx, 'Cref': Cxr, 'sig': _VF_SIG, 'labels': hns5} return pcolorshift
def _compute_f_stat(sample_size, num_groups, tri_idxs, distances, group_sizes, s_T, grouping, subjects, paired): """Compute PERMANOVA Pseudo-F.""" s_WG, s_WS, s_WG_V = _compute_s_W_S(sample_size, num_groups, tri_idxs, distances, group_sizes, grouping, subjects, paired) # for pseudo-F1: s_BG = s_T - s_WG # = s_Effect dfBG = (num_groups - 1) if (paired == True): s_BS = s_T - s_WS s_Error = s_WS - s_BG dfErr = (num_groups - 1) * (len(np.unique(subjects)) - 1) if np.isclose(s_Error, 0, atol=1e-9): s_Error = np.abs(s_Error) if (s_Error < 0): print('WARNING: s_Error = {:1.4f} < 0!'.format(s_Error)) print( ' s_BG = {:1.4f}, s_WG = {:1.4f}, s_BS = {:1.4f}, s_WS = {:1.4f}.' .format(s_BG, s_WG, s_BS, s_WS)) print( ' Setting s_Error to s_WGB (s_S -> 0) (cfr. paired = False)!' ) s_Error = s_WG s_BS = np.nan dfErr = (sample_size - num_groups) else: s_Error = s_WG # for pseudo-F1 s_Error2 = s_WG_V # for pseudo-F2 s_BS = np.nan dfErr = (sample_size - num_groups) # test statistic, pseudo-F1: stat_ = (s_BG / dfBG) / (s_Error / dfErr) if paired == True: # test statistic, pseudo-F2 (equals pseudo-F1 for equal sample sizes!): stat = stat_ else: # test statistic, pseudo-F2: stat = (s_BG) / (s_Error2) # effect sizes: p_eta2 = s_BG / (s_BG + s_Error) omega2 = (s_BG - dfBG * (s_Error / dfErr)) / (s_T - (s_Error / dfErr)) R2 = 1.0 - 1 / (1 + stat * (dfBG / dfErr)) #print('t:',sample_size, num_groups, (sample_size - num_groups - 1)) R2adj = 1.0 - ((1 - R2) * (sample_size - 1) / (sample_size - num_groups - 1)) effect_sizes = { 'p_eta2': p_eta2, 'omega2': omega2, 'R2': R2, 'R2adj': R2adj } # print('s_BG = {:1.2f}, s_WG = {:1.2f}, s_BS = {:1.2f}, s_WS = {:1.2f}, s_Err = {:1.2f} -- > s_T = {:1.2f}(Sum={:1.2f}:{:1.2f}).'.format(s_BG, s_WG, s_BS, s_WS, s_Error, s_T, s_BG + s_WG, s_BS + s_WS)) if s_Error < 0: print('WARNING: s_Error = {:1.4f} <= 0!'.format(s_Error)) print( ' s_BG = {:1.4f}, s_WG = {:1.4f}, s_BS = {:1.4f}, s_WS = {:1.4f}.' .format(s_BG, s_WG, s_BS, s_WS)) print(' Setting F to NaN.') stat = np.nan return stat, effect_sizes