def main(): # DCT1 on the test array # f = np.array([231, 32, 233, 161, 24, 71, 140, 245]) # c = DCT2_homemade.dct(f) # print(c) # f = DCT2_homemade.idct(c) # print(f) # DCT2 on the test matrix # f = np.matrix('231 32 233 161 24 71 140 245; 247 40 248 245 124 204 36 107; 234 202 245 167 9 217 239 173; 193 190 100 167 43 180 8 70; 11 24 210 177 81 243 8 112; 97 195 203 47 125 114 165 181; 193 70 174 167 41 30 127 245; 87 149 57 192 65 129 178 228') # c = DCT2_homemade.dct2(f) # print(c) # f = DCT2_homemade.idct2(c) # print(f) # c = DCT2_library.dctn(f, 2, norm='ortho') # print(c) # f = DCT2_library.idctn(c, 2, norm='ortho') # print(f) max_value = 255 times_DCT2_homemade = [] times_DCT2_library = [] n_min = 50 n_max = 500 steps = 50 for n in range(n_min, (n_max + 1), steps): f = np.matrix(np.random.randint(max_value, size=(n, n))).astype(float) begin_time = datetime.datetime.now() DCT2_homemade.dct2(f) time = datetime.datetime.now() - begin_time times_DCT2_homemade.append(time.seconds * 1000 + time.microseconds / 1000) print("Execution time of DCT2_homemade (n={}): {}".format(n, time)) begin_time = datetime.datetime.now() DCT2_library.dctn(f, 2, norm='ortho') time = datetime.datetime.now() - begin_time times_DCT2_library.append(time.seconds * 1000 + time.microseconds / 1000) print("Execution time of DCT2_library (n={}): {}".format(n, time)) palette = plt.get_cmap('Set1') plt.xlabel('N') plt.ylabel('Time (milliseconds in log scale)') plt.yscale('log') plt.grid(True, alpha=0.2) plt.plot(range(n_min, (n_max + 1), steps), times_DCT2_homemade, color=palette(1), linewidth=2) plt.plot(range(n_min, (n_max + 1), steps), times_DCT2_library, color=palette(4), linewidth=2) plt.legend(['DCT2_homemade', 'DCT2_library'], loc=2, ncol=2) plt.show()
def unwrap_phase_based_cosine_transform(mat, window=None): """ Unwrap a phase image using the cosine transform as described in Ref. [1]_. Parameters ---------- mat : array_like 2D array. Wrapped phase-image in the range of [-Pi; Pi]. window : array_like 2D array. Window is used for the cosine transform. Generated if None. Returns ------- array_like 2D array. Unwrapped phase-image. References ---------- .. [1] https://doi.org/10.1364/JOSAA.11.000107 """ (height, width) = mat.shape if window is None: window = _make_cosine_window(height, width) else: if window.shape != mat.shape: raise ValueError("Window must be the same size as the image!!!") rho_x = _wrap_to_pi(np.diff(mat, axis=1)) rho_y = _wrap_to_pi(np.diff(mat, axis=0)) rho_x2 = np.diff(rho_x, axis=1, prepend=0, append=0) rho_y2 = np.diff(rho_y, axis=0, prepend=0, append=0) rho = rho_x2 + rho_y2 mat_unwrap = idctn(dctn(rho) / window, overwrite_x=True) return mat_unwrap
def convJPG(im, Q): # Separarea canaleleor RGB r = im[:, :, 0] g = im[:, :, 1] b = im[:, :, 2] #Trecerea din RGB in YCbCr y, cb, cr = RGBtoYCbCr(r, g, b) for i in range(1, im.shape[0]): for j in range(1, im.shape[1]): if i % 8 == 0 and j % 8 == 0: xy = y[i - 8:i, j - 8:j] xcb = cb[i - 8:i, j - 8:j] xcr = cr[i - 8:i, j - 8:j] x = [xy, xcb, xcr] for k in range(len(x)): yt = dctn(x[k]) yjpeg = Q * (np.round(yt / Q)) xjpeg = idctn(yjpeg) x[k] = xjpeg y[i - 8:i, j - 8:j] = x[0] cb[i - 8:i, j - 8:j] = x[1] cr[i - 8:i, j - 8:j] = x[2] imr = im.copy() # Trecerea din YCrCb in RGB r, g, b = YCbCrtoRGB(y, cb, cr) imr[:, :, 0] = r imr[:, :, 1] = g imr[:, :, 2] = b return imr
def transform(self, obv: np.ndarray): obv = obv.astype(np.float32) if self.arrangement == "flipped_grid": top_row = np.concatenate([obv[0], np.fliplr(obv[1])], axis=1) bottom_row = np.concatenate( [np.flipud(obv[2]), np.fliplr(np.flipud(obv[3]))], axis=1) obv = np.concatenate([top_row, bottom_row], axis=0) elif self.arrangement == "grid": top_row = np.concatenate([obv[0], obv[1]], axis=1) bottom_row = np.concatenate([obv[2], obv[3]], axis=1) obv = np.concatenate([top_row, bottom_row], axis=0) elif self.arrangement == "stacked": obv = np.vstack(obv) elif self.arrangement == "row": obv = np.hstack(obv) elif self.arrangement == "flipped_grid": obv = np.concatenate( [obv[0], np.flipud(obv[1]), obv[2], np.flipud(obv[3])], axis=0) else: raise Exception("No valid transformatoin specified") proj = dctn(obv, norm="ortho", type=2, overwrite_x=True) proj = proj[1:self.cut + 1, 1:self.cut + 1].flatten() return proj
def solvePoisson_precomped(rho, scale): """Solve the poisson equation "P phi = rho" using DCT Uses precomputed scaling factors `scale` """ dctPhi = dctn(rho) / scale # now invert to get the result phi = idctn(dctPhi, overwrite_x=True) return phi
def low_removed(filename): img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE) height, width = img.shape[:2] dct_coef = dctn(img) dct_coef = dct_coef[30:, 30:] img = idctn(dct_coef) img = cv2.resize(img, dsize=(width, height), interpolation=cv2.INTER_LINEAR) cv2.imwrite("low_removed_" + filename, img)
def swap_dct_coef(): img_building = cv2.imread("img_building.png", cv2.IMREAD_GRAYSCALE) img_face = cv2.imread("img_face.png", cv2.IMREAD_GRAYSCALE) img_pattern = cv2.imread("img_pattern.png", cv2.IMREAD_GRAYSCALE) dct_building = dctn(img_building) dct_face = dctn(img_face) dct_pattern = dctn(img_pattern) # swap first 50x50 building and pattern coefficients dct_bp = dct_building.copy() dct_bp[:50, :50] = dct_pattern[:50, :50] img_bp = idctn(dct_bp) cv2.imwrite("swapped_bp.png", img_bp) # swap first 50x50 face and building coefficients dct_fb = dct_face.copy() dct_fb[:50, :50] = dct_building[:50, :50] img_fb = idctn(dct_fb) cv2.imwrite("swapped_fb.png", img_fb)
def low_frequencies(filename): img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE) height, width = img.shape[:2] low_width = width // 4 low_height = height // 4 dct_coef = dctn(img) dct_coef = dct_coef[:low_height, :low_width] img = idctn(dct_coef) img = cv2.resize(img, dsize=(width, height), interpolation=cv2.INTER_LINEAR) cv2.imwrite("low_" + filename, img)
def solvePoisson(rho): """Solve the poisson equation "P phi = rho" using DCT """ dctRho = dctn(rho) N, M = rho.shape I, J = np.ogrid[0:N, 0:M] with np.errstate(divide='ignore'): dctPhi = dctRho / 2 / (np.cos(np.pi * I / M) + np.cos(np.pi * J / N) - 2) dctPhi[0, 0] = 0 # handling the inf/nan value # now invert to get the result phi = idctn(dctPhi) return phi
def rescale_coef(filename): img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE) height, width = img.shape[:2] dct_coef = dctn(img) for i in range(dct_coef.shape[0]): for j in range(dct_coef.shape[1]): dist = (i**2 + j**2)**(1 / 3) if not np.isclose(dist, 0): dct_coef[i, j] /= dist img = idctn(dct_coef) img = cv2.resize(img, dsize=(width, height), interpolation=cv2.INTER_LINEAR) cv2.imwrite("rescaled_coef_" + filename, img)
def dct_hash(self, img, blur_dim=7): ''' :param img: (np.ndarray) the grayscale image to find a difference hash function of :param hash_size: (int) the number of bits in the hash (ex. setting to 8 yields 2**8=64 bit address) :param blur_dim(int) size of square mean-filter :return: (tuple(np.ndarray, np.ndarray)) 2 ** hash_size bit image hash binary array and DCT matrix output ''' if self.size != 8: print( "Original DCT was 8 x 8 so size parameter of ImageHash object may be off" ) dct_matx = dctn(cv2.blur(img, (blur_dim, blur_dim)), type=2, norm="ortho") tr_matx = dct_matx[:self.size, :self.size].flatten( ) # original algorithm selects top 8x8=64 return tr_matx > np.median(tr_matx), dct_matx
def middle_frequencies(filename): img = cv2.imread(filename, cv2.IMREAD_GRAYSCALE) height, width = img.shape[:2] dct_coef = dctn(img) new_coef = np.zeros((height - 100, width - 10)) height = (height - 150) // 2 width = (width - 150) // 2 new_coef[:height, :width] = dct_coef[:height, :width] common_height = min(dct_coef.shape[0], new_coef.shape[0]) common_width = min(dct_coef.shape[1], new_coef.shape[1]) new_coef[-height:, :common_width] = dct_coef[-height:, :common_width] new_coef[:common_height, -width:] = dct_coef[:common_height, -width:] img = idctn(new_coef) img = cv2.resize(img, dsize=(width, height), interpolation=cv2.INTER_LINEAR) cv2.imwrite("middle_" + filename, img)
def __call__(self, img): #Two dimensional discrete cosine transform #of image. Keep first (nk1,nk2) components Fkk = dctn(img, type=2, workers=-1, norm='forward') Fkk = Fkk[:self.size[0], :self.size[1]] return Fkk.reshape(-1)
def update_controls(self, target_belief, x_obs, x): ts = time.time() self.x = x nx, ny = target_belief.shape xmin, xmax, ymin, ymax = self.bounds L1 = xmax - xmin L2 = ymax - ymin if self.mode == 'mi': mu = convolve2d(target_belief, self.footprint_mask, mode='same') pos = self.tp_rate * mu + self.fp_rate * (1 - mu) mi = (-self.tp_rate * mu * np.log(pos / self.tp_rate) - (1 - self.tp_rate) * mu * np.log( (1 - pos) / (1 - self.tp_rate)) - self.fp_rate * (1 - mu) * np.log(pos / self.fp_rate) - (1 - self.fp_rate) * (1 - mu) * np.log((1 - pos) / (1 - self.fp_rate))) info = mi / np.sum(mi) elif self.mode == 'p': mu = convolve2d(target_belief, self.footprint_mask, mode='same') info = mu / np.sum(mu) elif self.mode == 'b': info = target_belief / np.sum(target_belief) elif self.mode == 'alpha': mu = convolve2d(target_belief, self.footprint_mask, mode='same') pos = self.tp_rate * mu + self.fp_rate * (1 - mu) alpha = .5 ami = (1 / (1 - alpha)) * (pos * np.log(mu * (self.tp_rate / pos)**alpha + (1 - mu) * (self.fp_rate / pos)**alpha) + (1 - pos) * np.log(mu * ((1 - self.tp_rate) / (1 - pos))**alpha + (1 - mu) * ((1 - self.fp_rate) / (1 - pos))**alpha)) info = ami / np.sum(ami) muk = dctn(info * np.sqrt(nx) * np.sqrt(ny), type=2, norm='ortho') muk = muk[0:self.nfourier, 0:self.nfourier].flatten() x1_comp = np.cos(np.outer((x_obs[:, 0] - xmin) / L1, self.k1)) x2_comp = np.cos(np.outer((x_obs[:, 1] - ymin) / L2, self.k2)) ck_prev = (1 / self.hk) * np.sum(x1_comp * x2_comp, axis=0) if len(self.ck_history_cum) == 0: self.ck_history_cum.append(ck_prev) ck_init = np.zeros(self.k1.shape[0]) t_total = self.t_horizon else: prev_cum = self.ck_history_cum[-1] self.ck_history_cum.append(prev_cum + ck_prev) if len(self.ck_history_cum) > self.t_history: ck_init = self.ck_history_cum[-1] - self.ck_history_cum[ -self.t_history - 1] t_total = self.t_history + self.t_horizon else: ck_init = self.ck_history_cum[-1] t_total = len(self.ck_history_cum) + self.t_horizon opti = casadi.Opti() v_u = [opti.variable(2, self.t_horizon) for i in range(self.nagents)] v_x = [casadi.cumsum(v_u[i], 2) + x[i, :] for i in range(self.nagents)] v_ck = (ck_init + (1 / self.hk) * sum([ casadi.sum2( casadi.cos(self.k1 @ (v_x[i][0, :] - xmin) / L1) * casadi.cos(self.k2 @ (v_x[i][1, :] - ymin) / L2)) for i in range(self.nagents) ])) / (self.nagents * t_total) erg_metric = casadi.sum1(self.Lambdak * (v_ck - muk)**2) change_penalties = [] for i in range(self.nagents): opti.subject_to(casadi.sum2(v_u[i]**2) < self.u_max**2) opti.subject_to(v_x[i][0, :] > xmin) opti.subject_to(v_x[i][0, :] < xmax) opti.subject_to(v_x[i][1, :] > ymin) opti.subject_to(v_x[i][1, :] < ymax) for j in range(i + 1, self.nagents): opti.subject_to( casadi.sum1((v_x[i] - v_x[j])**2) > self.min_dist**2) u_init = np.zeros((2, self.t_horizon)) u_init[:, 0:-1] = self.u[i][:, 1:] opti.set_initial(v_u[i], u_init) change_penalties.append( self.change_paramt0 * casadi.sum1(casadi.sum2((v_u[i][:, 0] - self.u[i][:, 0])**2))) change_penalties.append( self.change_param * casadi.sum1(casadi.sum2((v_u[i][:, 1:] - v_u[i][:, 0:-1])**2))) change_cost = sum(change_penalties) opti.minimize(erg_metric + change_cost) p_opts = {} # s_opts = {'max_cpu_time': .09, 'print_level': 0} s_opts = {'print_level': 0} opti.solver('ipopt', p_opts, s_opts) sol = opti.solve() self.u = [sol.value(v_u[i]) for i in range(self.nagents)] xs = [sol.value(v_x[i]) for i in range(self.nagents)] # try: # sol = opti.solve() # self.u = [sol.value(v_u[i]) for i in range(self.nagents)] # xs = [sol.value(v_x[i]) for i in range(self.nagents)] # except: # self.u = [opti.debug.value(v_u[i]) for i in range(self.nagents)] # xs = [opti.debug.value(v_x[i]) for i in range(self.nagents)] action = np.zeros((self.nagents, 2)) for i in range(self.nagents): action[i, :] = xs[i][:, 0] t_comp = time.time() - ts self.x = x if self.gui: xs_plot = [ np.insert(xs[i], 0, self.x[i, :], axis=1) for i in range(self.nagents) ] # display information map, planned trajectories, and if self.image1 is None: fig, (ax1, ax2) = plt.subplots(2, 1, num='erg', figsize=(2, 4)) self.image1 = ax1.imshow(info.T / np.max(info), extent=self.bounds, origin='lower', cmap='gray') self.image2 = ax2.imshow(target_belief.T / np.max(target_belief), extent=self.bounds, origin='lower', cmap='gray') ax1.set_title('Information Map') ax2.set_title('Target Belief') ax1.axis('off') ax2.axis('off') self.lines = [] self.inits = [] for i in range(self.nagents): line, = ax1.plot(xs[i][0, :], xs[i][1, :], 'r.-') #, linestyle=':') self.lines.append(line) init, = ax1.plot(self.x[i, 0], self.x[i, 1], 'g.') self.inits.append(init) else: plt.figure('erg') self.image1.set_data(info.T / np.max(info)) self.image2.set_data(target_belief.T / np.max(target_belief)) for i in range(self.nagents): self.lines[i].set_xdata(xs[i][0, :]) self.lines[i].set_ydata(xs[i][1, :]) self.inits[i].set_xdata(self.x[i, 0]) self.inits[i].set_ydata(self.x[i, 1]) plt.draw() plt.pause(.001) return action, t_comp
def comp_ch(ch, f): xch = dctn(ch) xch[f:] = 0 return idctn(xch)
def dct2(sub_img): return DCT2_library.dctn(sub_img, 2, norm='ortho')
def unwrap( f_wrapped, phi_x=None, phi_y=None, max_iters=500, tol=np.pi / 5, lmbda=1, p=0, c=1.3, dtype="float32", debug=False, # boundary_conditions="neumann", ): """Unwrap interferogram phase Parameters ---------- f_wrapped (ndarray): wrapped phase image (interferogram) phi_x (ndarray): estimate of the x-derivative of the wrapped phase If not passed, will compute using `est_wrapped_gradient` phi_y (ndarray): estimate of the y-derivative of the wrapped phase If not passed, will compute using `est_wrapped_gradient` max_iters (int): maximum number of ADMM iterations to run tol (float): maximum allowed change for any pixel between ADMM iterations lmbda (float): splitting parameter of ADMM. Smaller = more stable, Larger = faster convergence. p (float): value used in shrinkage operator c (float): acceleration constant using in updating lagrange multipliers in ADMM dtype: numpy datatype for output debug (bool): print diagnostic ADMM information """ rows, columns = f_wrapped.shape num = rows * columns if dtype is None: dtype = f_wrapped.dtype else: f_wrapped = f_wrapped.astype(dtype) boundary_conditions = "neumann" if debug: print(f"Making Dx, Dy with BCs={boundary_conditions}") Dx, Dy = make_differentiation_matrices( *f_wrapped.shape, boundary_conditions=boundary_conditions) if phi_x is None or phi_y is None: phi_x, phi_y = est_wrapped_gradient(f_wrapped, Dx, Dy, dtype=dtype) # Lagrange multiplier variables Lambda_x = np.zeros_like(phi_x, dtype=dtype) Lambda_y = np.zeros_like(phi_y, dtype=dtype) # aux. variables for ADMM, holding difference between # unwrapped phase gradient and measured gradient from igram w_x = np.zeros_like(phi_x, dtype=dtype) w_y = np.zeros_like(phi_y, dtype=dtype) F_old = np.zeros_like(f_wrapped) # Get K ready once for solving linear system K = make_laplace_kernel(rows, columns, dtype=dtype) for iteration in range(max_iters): # update Unwrapped Phase F: solve linear eqn in fourier domain # rhs = dx.T @ phi[0].ravel() + dy.T @ phi[1].ravel() rx = w_x.ravel() + phi_x.ravel() - Lambda_x.ravel() ry = w_y.ravel() + phi_y.ravel() - Lambda_y.ravel() RHS = Dx.T * rx + Dy.T * ry # Use DCT for neumann: rho_hat = dctn(RHS.reshape(rows, columns), type=2, norm='ortho', workers=-1) F = idctn(rho_hat * K, type=2, norm='ortho', workers=-1) # calculate x, y gradients of new unwrapped phase estimate Fx = (Dx @ F.ravel()).reshape(rows, columns) Fy = (Dy @ F.ravel()).reshape(rows, columns) input_x = Fx - phi_x + Lambda_x input_y = Fy - phi_y + Lambda_y w_x, w_y = p_shrink(np.stack((input_x, input_y), axis=0), lmbda=lmbda, p=p, epsilon=0) # update lagrange multipliers Lambda_x += c * (Fx - phi_x - w_x) Lambda_y += c * (Fy - phi_y - w_y) change = np.max(np.abs(F - F_old)) if debug: print(f"Iteration:{iteration} change={change}") if change < tol or np.isnan(change): break else: F_old = F if debug: print(f"Finished after {iteration} with change={change}") return F
def kde2d(x, y, n=256, limits=None): """ Return the 2d density map from discrete observations via 2-dimensional diffusion Kernel density estimation. First the input data is binned. After binning, the function determines the optimal bandwidth according to the diffusion-based method. It then smooths the binned data over the grid using a Gaussian kernel with a standard deviation corresponding to that bandwidth. This module is based on the KDE-diffusion of Z. I. Botev, J. F. Grotowski, D. P. Kroese: Kernel density estimation via diffusion. Annals of Statistics 38 (2010), no. 5, 2916--2957. doi:10.1214/10-AOS799 and John Hennig DOI: 10.5281/zenodo.3830437 https://doi.org/10.5281/zenodo.3830437 **Parameters** x A lists of array of numbers that represent discrete observations of a random variable with two coordinate components. The observations are binned on a grid of n*n points, where ``n`` must be a power of 2 or will be coerced to the next one. If ``x`` and ``y`` are not the same length, the algorithm will raise a ``ValueError``. y A lists of array of numbers that represent discrete observations of a random variable with two coordinate components. The observations are binned on a grid of n*n points, where ``n`` must be a power of 2 or will be coerced to the next one. If ``x`` and ``y`` are not the same length, the algorithm will raise a ``ValueError``. n (optional) The number of grid points. It must be a power of 2. Otherwise, it will be coerced to the next power of two. The default is 256. limits (optional) Data ``limits`` specified as a tuple of tuples denoting ``((xmin, xmax), (ymin, ymax))``. If any of the values are ``None``, they will be inferred from the data. Each tuple, or even both of them, may also be replaced by a single value denoting the upper bound of a range centered at zero. The default is ``None``. **Returns** A tuple whose elements are the following: density The density map of the data. grid The grid at which the density is computed. bandwidth The optimal values (per axis) that the algorithm has determined. If the algorithm does not converge, it will raise a ``ValueError``. --------------------------------------------------------------------------- """ # Convert to arrays in case lists are passed in. x = np.array(x) y = np.array(y) # Make sure numbers of data points are consistent. N = len(x) if len(y) != N: raise ValueError('x and y must have the same length.') # Round up number of bins to next power of two. n = int(2**np.ceil(np.log2(n))) # Determine missing data limits. if limits is None: xmin = xmax = ymin = ymax = None elif isinstance(limits, tuple): (xlimits, ylimits) = limits if xlimits is None: xmin = xmax = None elif isinstance(xlimits, tuple): (xmin, xmax) = xlimits else: xmin = -xlimits xmax = +xlimits if ylimits is None: ymin = ymax = None elif isinstance(ylimits, tuple): (ymin, ymax) = ylimits else: ymin = -ylimits ymax = +ylimits else: xmin = -limits xmax = +limits ymin = -limits ymax = +limits if None in (xmin, xmax): delta = x.max() - x.min() if xmin is None: xmin = x.min() - delta / 4 if xmax is None: xmax = x.max() + delta / 4 if None in (ymin, ymax): delta = y.max() - y.min() if ymin is None: ymin = y.min() - delta / 4 if ymax is None: ymax = y.max() + delta / 4 deltax = xmax - xmin deltay = ymax - ymin # Bin samples on regular grid. (binned, xedges, yedges) = np.histogram2d(x, y, bins=n, range=((xmin, xmax), (ymin, ymax))) grid = (xedges[:-1], yedges[:-1]) # Compute discrete cosine transform. Adjust first component. transformed = dctn(binned / N) transformed[0, :] /= 2 transformed[:, 0] /= 2 # Pre-compute squared indices and transform components before solver loop. k = np.arange(n, dtype="float") # float avoids integer overflow. k2 = k**2 a2 = transformed**2 # Define internal functions to be solved iteratively. def γ(t): sigma = psi(0, 2, t) + psi(2, 0, t) + 2 * psi(1, 1, t) γ = (2 * np.pi * N * sigma)**(-1 / 3) return (t - γ) / γ def psi(i, j, t): if i + j <= 4: sigma = abs(psi(i + 1, j, t) + psi(i, j + 1, t)) C = (1 + 1 / 2**(i + j + 1)) / 3 pii = np.product(np.arange(1, 2 * i, 2)) pij = np.product(np.arange(1, 2 * j, 2)) t = (C * pii * pij / (np.pi * N * sigma))**(1 / (2 + i + j)) w = 0.5 * np.ones(n) w[0] = 1 w = w * np.exp(-np.pi**2 * k2 * t) wx = w * k2**i wy = w * k2**j return (-1)**(i + j) * np.pi**(2 * (i + j)) * wy @ a2 @ wx # Solve for optimal diffusion time t*. try: ts = brentq(lambda t: t - γ(t), 0, 0.1) except ValueError: raise ValueError('Bandwidth optimization did not converge.') from None # Calculate diffusion times along x- and y-axis. psi02 = psi(0, 2, ts) psi20 = psi(2, 0, ts) psi11 = psi(1, 1, ts) tx1 = (psi02**(3 / 4) / (4 * np.pi * N * psi20**(3 / 4) * (psi11 + np.sqrt(psi02 * psi20))))**(1 / 3) tx2 = (psi20**(3 / 4) / (4 * np.pi * N * psi02**(3 / 4) * (psi11 + np.sqrt(psi02 * psi20))))**(1 / 3) # Note: # The above uses the nomenclature from the paper. In the Matlab # reference, tx1 is called t_y, while tx2 is t_x. This is a curious # change in notation. It may be related to the fact that image # coordinates are typically in (y,x) index order, whereas matrices, # such as the binned histogram (in Matlab as much as in Python), # are in (x,y) order. The Matlab code eventually does return # image-like index order, though it never explicitly transposes # the density matrix. That is implicitly handled by its custom # implementation of the inverse transformation (idct2d), which # only employs one matrix transposition, not two as its forward # counterpart (dct2d). # Apply Gaussian filter with optimized kernel. smoothed = transformed * np.outer(np.exp(-np.pi**2 * k2 * tx2 / 2), np.exp(-np.pi**2 * k2 * tx1 / 2)) # Reverse transformation. smoothed[0, :] *= 2 smoothed[:, 0] *= 2 inverse = idctn(smoothed) # Normalize density. density = np.transpose(inverse) * n / deltax * n / deltay # Determine bandwidth from diffusion times. bandwidth = np.array([np.sqrt(tx2) * deltax, np.sqrt(tx1) * deltay]) # Return results. return (density, grid, bandwidth)
def kde2d(x, y, n=256, limits=None): """ Estimates the 2d density from discrete observations. The input is two lists/arrays `x` and `y` of numbers that represent discrete observations of a random variable with two coordinate components. The observations are binned on a grid of n×n points. `n` will be coerced to the next highest power of two if it isn't one to begin with. Data `limits` may be specified as a tuple of tuples denoting `((xmin, xmax), (ymin, ymax))`. If any of the values are `None`, they will be inferred from the data. Each tuple, or even both of them, may also be replaced by a single value denoting the upper bound of a range centered at zero. After binning, the function determines the optimal bandwidth according to the diffusion-based method. It then smooths the binned data over the grid using a Gaussian kernel with a standard deviation corresponding to that bandwidth. Returns the estimated `density` and the `grid` (along each of the two axes) upon which it was computed, as well as the optimal `bandwidth` values (per axis) that the algorithm determined. Raises `ValueError` if the algorithm did not converge or `x` and `y` are not the same length. """ # Convert to arrays in case lists are passed in. x = array(x) y = array(y) # Make sure numbers of data points are consistent. N = len(x) if len(y) != N: raise ValueError('x and y must have the same length.') # Round up number of bins to next power of two. n = int(2**ceil(log2(n))) # Determine missing data limits. if limits is None: xmin = xmax = ymin = ymax = None elif isinstance(limits, tuple): (xlimits, ylimits) = limits if xlimits is None: xmin = xmax = None elif isinstance(xlimits, tuple): (xmin, xmax) = xlimits else: xmin = -xlimits xmax = +xlimits if ylimits is None: ymin = ymax = None elif isinstance(ylimits, tuple): (ymin, ymax) = ylimits else: ymin = -ylimits ymax = +ylimits else: xmin = -limits xmax = +limits ymin = -limits ymax = +limits if None in (xmin, xmax): delta = x.max() - x.min() if xmin is None: xmin = x.min() - delta / 4 if xmax is None: xmax = x.max() + delta / 4 if None in (ymin, ymax): delta = y.max() - y.min() if ymin is None: ymin = y.min() - delta / 4 if ymax is None: ymax = y.max() + delta / 4 Δx = xmax - xmin Δy = ymax - ymin # Bin samples on regular grid. (binned, xedges, yedges) = histogram2d(x, y, bins=n, range=((xmin, xmax), (ymin, ymax))) grid = (xedges[:-1], yedges[:-1]) # Compute discrete cosine transform, then adjust first component. transformed = dctn(binned / N) transformed[0, :] /= 2 transformed[:, 0] /= 2 # Pre-compute squared indices and transform components before solver loop. k = arange(n, dtype='float') # "float" avoids integer overflow. k2 = k**2 a2 = transformed**2 # Define internal functions to be solved iteratively. def γ(t): Σ = ψ(0, 2, t) + ψ(2, 0, t) + 2 * ψ(1, 1, t) γ = (2 * π * N * Σ)**(-1 / 3) return (t - γ) / γ def ψ(i, j, t): if i + j <= 4: Σ = abs(ψ(i + 1, j, t) + ψ(i, j + 1, t)) C = (1 + 1 / 2**(i + j + 1)) / 3 Πi = product(arange(1, 2 * i, 2)) Πj = product(arange(1, 2 * j, 2)) t = (C * Πi * Πj / (π * N * Σ))**(1 / (2 + i + j)) w = 0.5 * ones(n) w[0] = 1 w = w * exp(-π**2 * k2 * t) wx = w * k2**i wy = w * k2**j return (-1)**(i + j) * π**(2 * (i + j)) * wy @ a2 @ wx # Solve for optimal diffusion time t*. try: ts = brentq(lambda t: t - γ(t), 0, 0.1) except ValueError: raise ValueError('Bandwidth optimization did not converge.') from None # Calculate diffusion times along x- and y-axis. ψ02 = ψ(0, 2, ts) ψ20 = ψ(2, 0, ts) ψ11 = ψ(1, 1, ts) tx1 = (ψ02**(3 / 4) / (4 * π * N * ψ20**(3 / 4) * (ψ11 + sqrt(ψ02 * ψ20))))**(1 / 3) tx2 = (ψ20**(3 / 4) / (4 * π * N * ψ02**(3 / 4) * (ψ11 + sqrt(ψ02 * ψ20))))**(1 / 3) # Note: # The above uses the nomenclature from the paper. In the Matlab # reference, tx1 is called t_y, while tx2 is t_x. This is a curious # change in notation. It may be related to the fact that image # coordinates are typically in (y,x) index order, whereas matrices, # such as the binned histogram (in Matlab as much as in Python), # are in (x,y) order. The Matlab code eventually does return # image-like index order, though it never explicitly transposes # the density matrix. That is implicitly handled by its custom # implementation of the inverse transformation (idct2d), which # only employs one matrix transposition, not two as its forward # counterpart (dct2d). # Apply Gaussian filter with optimized kernel. smoothed = transformed * outer(exp(-π**2 * k2 * tx2 / 2), exp(-π**2 * k2 * tx1 / 2)) # Reverse transformation after adjusting first component. smoothed[0, :] *= 2 smoothed[:, 0] *= 2 inverse = idctn(smoothed) # Normalize density. density = inverse * n / Δx * n / Δy # Determine bandwidth from diffusion times. bandwidth = array([sqrt(tx2) * Δx, sqrt(tx1) * Δy]) # Return results. return (density, grid, bandwidth)
import numpy as np import matplotlib.pyplot as plt from scipy import misc, ndimage from scipy.fft import dctn, idctn X = misc.ascent() plt.imshow(X, cmap=plt.cm.gray) plt.show() Y1 = dctn(X, type=1) Y2 = dctn(X, type=2) Y3 = dctn(X, type=3) Y4 = dctn(X, type=4) freq_db_1 = 20 * np.log10(abs(Y1)) freq_db_2 = 20 * np.log10(abs(Y2)) freq_db_3 = 20 * np.log10(abs(Y3)) freq_db_4 = 20 * np.log10(abs(Y4)) plt.subplot(221).imshow(freq_db_1) plt.subplot(222).imshow(freq_db_2) plt.subplot(223).imshow(freq_db_3) plt.subplot(224).imshow(freq_db_4) plt.show() k = 120 Y_ziped = Y2.copy() Y_ziped[k:] = 0 X_ziped = idctn(Y_ziped) plt.imshow(X_ziped, cmap=plt.cm.gray)
assert n == ref['n'] assert n == ref['density'].shape[0] # Determine data ranges, required for scaling. Δx = xmax - xmin Δy = ymax - ymin # Bin samples on regular grid. (binned, xedges, yedges) = histogram2d(x, y, bins=n, range=((xmin, xmax), (ymin, ymax))) assert isclose(binned / N, ref['initial_data']).all() # Compute 2d discrete cosine transform. transformed = dctn(binned / N) transformed[0, :] /= 2 transformed[:, 0] /= 2 assert isclose(transformed, ref['a']).all() # Pre-compute squared indices and transform components before solver loop. k = arange(n, dtype='float') # float avoids integer overflow. k2 = k**2 a2 = transformed**2 # Define internal functions to be solved iteratively. def γ(t): Σ = ψ(0, 2, t) + ψ(2, 0, t) + 2 * ψ(1, 1, t) γ = (2 * π * N * Σ)**(-1 / 3) return (t - γ) / γ