def _hue_bin_data_to_ellipsefit(hue_bin_data): # use get chroma-normalized jabtn_hj: jabt = hue_bin_data['jabtn_hj'] ecc = np.ones((1,jabt.shape[1]))*np.nan theta = np.ones((1,jabt.shape[1]))*np.nan v = np.ones((jabt.shape[1],5))*np.nan for i in range(jabt.shape[1]): try: v[i,:] = math.fit_ellipse(jabt[:,i,1:]) a,b = v[i,0], v[i,1] # major and minor ellipse axes ecc[0,i] = a/b theta[0,i] = np.rad2deg(v[i,4]) # orientation angle if theta[0,i]>180: theta[0,i] = theta[0,i] - 180 except: v[i,:] = np.nan*np.ones((1,5)) ecc[0,i] = np.nan theta[0,i] = np.nan # orientation angle return {'v':v, 'a/b':ecc,'thetad': theta}
def plotellipse(v, cspace_in = 'Yxy', cspace_out = None, nsamples = 100, \ show = True, axh = None, \ line_color = 'darkgray', line_style = ':', line_width = 1, line_marker = '', line_markersize = 4,\ plot_center = False, center_marker = 'o', center_color = 'darkgray', center_markersize = 4,\ show_grid = False, llabel = '', label_fontname = 'Times New Roman', label_fontsize = 12,\ out = None): """ Plot ellipse(s) given in v-format [Rmax,Rmin,xc,yc,theta]. Args: :v: | (Nx5) ndarray | ellipse parameters [Rmax,Rmin,xc,yc,theta] :cspace_in: | 'Yxy', optional | Color space of v. | If None: no color space assumed. Axis labels assumed ('x','y'). :cspace_out: | None, optional | Color space to plot ellipse(s) in. | If None: plot in cspace_in. :nsamples: | 100 or int, optional | Number of points (samples) in ellipse boundary :show: | True or boolean, optional | Plot ellipse(s) (True) or not (False) :axh: | None, optional | Ax-handle to plot ellipse(s) in. | If None: create new figure with axes. :line_color: | 'darkgray', optional | Color to plot ellipse(s) in. :line_style: | ':', optional | Linestyle of ellipse(s). :line_width': | 1, optional | Width of ellipse boundary line. :line_marker: | 'none', optional | Marker for ellipse boundary. :line_markersize: | 4, optional | Size of markers in ellipse boundary. :plot_center: | False, optional | Plot center of ellipse: yes (True) or no (False) :center_color: | 'darkgray', optional | Color to plot ellipse center in. :center_marker: | 'o', optional | Marker for ellipse center. :center_markersize: | 4, optional | Size of marker of ellipse center. :show_grid: | False, optional | Show grid (True) or not (False) :llabel: | None,optional | Legend label for ellipse boundary. :label_fontname: | 'Times New Roman', optional | Sets font type of axis labels. :label_fontsize: | 12, optional | Sets font size of axis labels. :out: | None, optional | Output of function | If None: returns None. Can be used to output axh of newly created | figure axes or to return Yxys an ndarray with coordinates of | ellipse boundaries in cspace_out (shape = (nsamples,3,N)) Returns: :returns: None, or whatever set by :out:. """ Yxys = np.zeros((nsamples, 3, v.shape[0])) ellipse_vs = np.zeros((v.shape[0], 5)) for i, vi in enumerate(v): # Set sample density of ellipse boundary: t = np.linspace(0, 2 * np.pi, int(nsamples)) a = vi[0] # major axis b = vi[1] # minor axis xyc = vi[2:4, None] # center theta = vi[-1] # rotation angle # define rotation matrix: R = np.hstack((np.vstack((np.cos(theta), np.sin(theta))), np.vstack((-np.sin(theta), np.cos(theta))))) # Calculate ellipses: Yxyc = np.vstack((1, xyc)).T Yxy = np.vstack( (np.ones((1, nsamples)), xyc + np.dot(R, np.vstack((a * np.cos(t), b * np.sin(t)))))).T Yxys[:, :, i] = Yxy # Convert to requested color space: if (cspace_out is not None) & (cspace_in is not None): Yxy = colortf(Yxy, cspace_in + '>' + cspace_out) Yxyc = colortf(Yxyc, cspace_in + '>' + cspace_out) Yxys[:, :, i] = Yxy # get ellipse parameters in requested color space: ellipse_vs[i, :] = math.fit_ellipse(Yxy[:, 1:]) #de = np.sqrt((Yxy[:,1]-Yxyc[:,1])**2 + (Yxy[:,2]-Yxyc[:,2])**2) #ellipse_vs[i,:] = np.hstack((de.max(),de.min(),Yxyc[:,1],Yxyc[:,2],np.nan)) # nan because orientation is xy, but request is some other color space. Change later to actual angle when fitellipse() has been implemented # plot ellipses: if show == True: if (axh is None) & (i == 0): fig = plt.figure() axh = fig.add_subplot(111) if (cspace_in is None): xlabel = 'x' ylabel = 'y' else: xlabel = _CSPACE_AXES[cspace_in][1] ylabel = _CSPACE_AXES[cspace_in][2] if (cspace_out is not None): xlabel = _CSPACE_AXES[cspace_out][1] ylabel = _CSPACE_AXES[cspace_out][2] if plot_center == True: axh.plot(Yxyc[:, 1], Yxyc[:, 2], color=center_color, linestyle='none', marker=center_marker, markersize=center_markersize) if llabel is None: axh.plot(Yxy[:, 1], Yxy[:, 2], color=line_color, linestyle=line_style, linewidth=line_width, marker=line_marker, markersize=line_markersize) else: axh.plot(Yxy[:, 1], Yxy[:, 2], color=line_color, linestyle=line_style, linewidth=line_width, marker=line_marker, markersize=line_markersize, label=llabel) axh.set_xlabel(xlabel, fontname=label_fontname, fontsize=label_fontsize) axh.set_ylabel(ylabel, fontname=label_fontname, fontsize=label_fontsize) if show_grid == True: plt.grid(True) #plt.show() Yxys = np.transpose(Yxys, axes=(0, 2, 1)) if out is not None: return eval(out) else: return None
def _tm30_process_spd(spd, cri_type = 'ies-tm30',**kwargs): """ Calculate all required parameters for plotting from spd using cri.spd_to_cri() Args: :spd: | ndarray or dict | If ndarray: single spectral power distribution. | If dict: dictionary with pre-computed parameters. | required keys: | 'Rf','Rg','cct','duv','Sr','cri_type','xyzri','xyzrw', | 'hbinnrs','Rfi','Rfhi','Rcshi','Rhshi', | 'jabt_binned','jabr_binned', | 'nhbins','start_hue','normalize_gamut','normalized_chroma_ref' | see cri.spd_to_cri() for more info on parameters. :cri_type: | _CRI_TYPE_DEFAULT or str or dict, optional | -'str: specifies dict with default cri model parameters | (for supported types, see luxpy.cri._CRI_DEFAULTS['cri_types']) | - dict: user defined model parameters | (see e.g. luxpy.cri._CRI_DEFAULTS['cierf'] | for required structure) | Note that any non-None input arguments (in kwargs) | to the function will override default values in cri_type dict. :kwargs: | Additional optional keyword arguments, | the same as in cri.spd_to_cri() Returns: :data: | dictionary with required parameters for plotting functions. """ out = 'Rf,Rg,cct,duv,Sr,cri_type,xyzri,xyzrw,binnrs,Rfi,Rfhi,Rcshi,Rhshi,jabt_binned,jabr_binned,nhbins,start_hue,normalize_gamut,normalized_chroma_ref' if not isinstance(spd,dict): tpl = spd_to_cri(spd, cri_type = cri_type, out = out, **kwargs) data = {'spd':spd} for i,key in enumerate(out.split(',')): if key == 'normalized_chroma_ref': key = 'scalef' # rename if key == 'binnrs': key = 'hbinnrs' # rename data[key] = tpl[i] # Normalize chroma to scalef and fit ellipse to gamut: scalef = data['scalef'] jabt = data['jabt_binned'].copy() jabr = data['jabr_binned'].copy() Cr = (jabr[...,1]**2 + jabr[...,2]**2)**0.5 Ct = ((jabt[...,1]**2 + jabt[...,2]**2)**0.5)/Cr*scalef ht = math.positive_arctan(jabt[...,1],jabt[...,2], htype = 'rad') hr = math.positive_arctan(jabr[...,1],jabr[...,2], htype = 'rad') jabt[...,1] = Ct*np.cos(ht) jabt[...,2] = Ct*np.sin(ht) jabr[...,1] = scalef*np.cos(hr) jabr[...,2] = scalef*np.sin(hr) ecc = np.ones((1,jabt.shape[1]))*np.nan theta = np.ones((1,jabt.shape[1]))*np.nan v = np.ones((jabt.shape[1],5))*np.nan data['hue_bin_data'] = {'jabt_hj_closed':data['jabt_binned'],'jabr_hj_closed':data['jabr_binned'], 'jabtn_hj_closed':jabt,'jabrn_hj_closed':jabr} for i in range(jabt.shape[1]): try: v[i,:] = math.fit_ellipse(jabt[:,i,1:]) a,b = v[i,0], v[i,1] # major and minor ellipse axes ecc[0,i] = a/b theta[0,i] = np.rad2deg(v[i,4]) # orientation angle if theta[0,i]>180: theta[0,i] -= 180 except: v[i,:] = np.nan*np.ones((1,5)) ecc[0,i] = np.nan theta[0,i] = np.nan # orientation angle data['gamut_ellipse_fit'] = {'v':v, 'a/b':ecc,'thetad': theta} else: data = spd return data