def affineHomographyBlocks(coordinate_list): """Generate affine homography blocks which can be used to solve for homography coordinates.""" # Store dtype and backend dtype = yp.getDatatype(coordinate_list) backend = yp.getBackend(coordinate_list) # Convert coordinates to numpy array coordinate_list = yp.asarray(yp.real(coordinate_list), dtype='float32', backend='numpy') # Ensure coordinate_list is a list of lists if yp.ndim(coordinate_list) == 1: coordinate_list = np.asarray([coordinate_list]) # Determine the number of elements in a coordinate axis_count = yp.shape(coordinate_list)[1] # Loop over positions and concatenate to array coordinate_blocks = [] for coordinate in coordinate_list: block = np.append(coordinate, 1) coordinate_blocks.append(np.kron(np.eye(len(coordinate)), block)) # Convert to initial datatype and backend coordinate_blocks = yp.asarray(np.concatenate(coordinate_blocks), dtype, backend) return coordinate_blocks
def L2Sequential(A_list, y_list, reg=None): """ L2 objective function (sequential solver) """ objective_function_list = [] for index in range(len(A_list)): # Assign local operator and measurement A, y = A_list[index], y_list[index] # Check backend and datatype assert A.backend == getBackend(y) assert A.dtype == getDatatype(y) # Form local objective function and append objective_function_list.append(L2(A, y, reg)) # Return stacked list return Vstack(objective_function_list)
def VecStack(vector_list, axis=0): """ This is a helper function to stack vectors """ # Determine output size single_vector_shape = [ max([shape(vector)[0] for vector in vector_list]), max([shape(vector)[1] for vector in vector_list]) ] vector_shape = dcopy(single_vector_shape) vector_shape[axis] *= len(vector_list) # Allocate memory vector_full = zeros(vector_shape, getDatatype(vector_list[0]), getBackend(vector_list[0])) # Assign vector elements to the output for index, vector in enumerate(vector_list): vector_full[index * single_vector_shape[0]:(index + 1) * single_vector_shape[0], :] = pad(vector, single_vector_shape) # Return return vector_full
def L2(A, y, reg=None): """ L2 objective function (global solver)""" # assert A.M == shape(y), "Operator first dimension %d is not operator shape %s" % (A.shape[0], shape(y)) assert A.backend == getBackend(y) assert A.dtype == getDatatype(y) # Define objective function L2_op = L2Norm(A.M, A.dtype, A.backend) objective_data_term = L2_op * (A - y) # Add L2 regularization if desired if reg is not None: # Create full objective (for gradient descent) objective = objective_data_term + reg * L2Norm(A.shape[1], A.dtype, A.backend) # Add regularization to data term (only used for inverse) objective_data_term.inverse_regularizer = reg else: objective = objective_data_term # Simply return this operator return _addInvertFunction(objective)
def demosaic(frame, order='grbg', bayer_coupling_matrix=None, debug=False, white_balance=False): # bayer_coupling_matrix = None # bgrg: cells very green # rggb: slight gteen tint """Demosaic a frame""" frame_out = yp.zeros((int(yp.shape(frame)[0] / 2), int(yp.shape(frame)[1] / 2), 3), yp.getDatatype(frame), yp.getBackend(frame)) if bayer_coupling_matrix is not None: frame_vec = yp.zeros((4, int(yp.shape(frame)[0] * yp.shape(frame)[1] / 4)), yp.getDatatype(frame), yp.getBackend(frame)) # Cast bayer coupling matrix bayer_coupling_matrix = yp.cast(bayer_coupling_matrix, yp.getDatatype(frame), yp.getBackend(frame)) # Define frame vector for bayer_pattern_index in range(4): pixel_offsets = (0, 0) if bayer_pattern_index == 3: img_sub = frame[pixel_offsets[0]::2, pixel_offsets[1]::2] elif bayer_pattern_index == 1: img_sub = frame[pixel_offsets[0]::2, pixel_offsets[1] + 1::2] elif bayer_pattern_index == 2: img_sub = frame[pixel_offsets[0] + 1::2, pixel_offsets[1]::2] elif bayer_pattern_index == 0: img_sub = frame[pixel_offsets[0] + 1::2, pixel_offsets[1] + 1::2] frame_vec[bayer_pattern_index, :] = yp.dcopy(yp.vec(img_sub)) if debug: print("Channel %d mean is %g" % (bayer_pattern_index, yp.scalar(yp.real(yp.sum(img_sub))))) # Perform demosaic using least squares result = yp.linalg.lstsq(bayer_coupling_matrix, frame_vec) result -= yp.amin(result) result /= yp.amax(result) for channel in range(3): values = result[channel] frame_out[:, :, channel] = yp.reshape(values, ((yp.shape(frame_out)[0], yp.shape(frame_out)[1]))) if white_balance: frame_out[:, :, channel] -= yp.amin(frame_out[:, :, channel]) frame_out[:, :, channel] /= yp.amax(frame_out[:, :, channel]) return frame_out else: frame_out = yp.zeros((int(yp.shape(frame)[0] / 2), int(yp.shape(frame)[1] / 2), 3), dtype=yp.getDatatype(frame), backend=yp.getBackend(frame)) # Get color order from order variable b_index = order.find('b') r_index = order.find('r') g1_index = order.find('g') # Get g2 from intersection of sets g2_index = set(list(range(4))).difference({b_index, r_index, g1_index}).pop() # +-----+-----+ # | 0 | 1 | # +-----+-----| # | 2 | 3 | # +-----+-----| if debug: import matplotlib.pyplot as plt plt.figure() plt.imshow(frame[:12, :12]) r_start = (int(r_index in [2, 3]), int(r_index in [1, 3])) g1_start = (int(g1_index in [2, 3]), int(g1_index in [1, 3])) g2_start = (int(g2_index in [2, 3]), int(g2_index in [1, 3])) b_start = (int(b_index in [2, 3]), int(b_index in [1, 3])) frame_out[:, :, 0] = frame[r_start[0]::2, r_start[1]::2] frame_out[:, :, 1] = (frame[g1_start[0]::2, g1_start[1]::2] + frame[g2_start[0]::2, g2_start[1]::2]) / 2.0 frame_out[:, :, 2] = frame[b_start[0]::2, b_start[1]::2] # normalize frame_out /= yp.max(frame_out) # Perform white balancing if desired if white_balance: clims = [] for channel in range(3): clims.append(yp.max(frame_out[:, :, channel])) frame_out[:, :, channel] /= yp.max(frame_out[:, :, channel]) # Return frame return frame_out
def ConvolutionOld(kernel, dtype=None, backend=None, normalize=False, mode='circular', label='C', pad_value='mean', pad_size=None, fft_backend=None, inverse_regularizer=0, center=False, inner_operator=None, force_full_convolution=False): """Convolution linear operator""" # Get temporary kernel to account for inner_operator size _kernel = kernel if inner_operator is None else inner_operator * kernel # Check number of dimensions N = _kernel.shape dim_count = len(N) assert dim_count == ndim(_kernel) # Get kernel backend if backend is None: backend = getBackend(kernel) else: # Convert kernel to provided backend kernel = asbackend(kernel, backend) # Get kernel dtype if dtype is None: dtype = getDatatype(kernel) else: kernel = astype(kernel, dtype) # Determine if the kernel is a shifted delta function - if so, return a # shift operator masked as a convolution position_list = tuple([ tuple(np.asarray(pos) - np.asarray(shape(_kernel)) // 2) for pos in where(_kernel != 0.0) ]) mode = 'discrete' if len( position_list) == 1 and not force_full_convolution else mode # Discrete convolution if mode == 'discrete': # Create shift operator, or identity operator if there is no shift. if all([pos == 0.0 for pos in position_list[0]]): op = Identity(N) else: op = Shift(N, position_list[0]) loc = where(_kernel != 0)[0] # If the kernel is not binary, normalize to the correct value if scalar(_kernel[loc[0], loc[1]]) != 1.0: op *= scalar(_kernel[loc[0], loc[1]]) # Update label to indicate this is a shift-based convolution label += '_{shift}' # Normalize if desired if normalize: op *= 1 / np.sqrt(np.size(kernel)) elif mode in ['windowed', 'circular']: # The only difference between circular and non-circular convolution is # the pad size. We'll define this first, then define the convolution in # a common framework. if mode == 'circular': # Pad kernel to effecient size N_pad = list(N) for ind, d in enumerate(N): if next_fast_len(d) != d: N_pad[ind] = next_fast_len(d) crop_start = [0] * len(N_pad) elif mode == 'windowed': if pad_size is None: # Determine support of kernel kernel_support_roi = boundingBox(kernel, return_roi=True) N_pad_raw = (np.asarray(N) + np.asarray(kernel_support_roi.size).tolist()) N_pad = [next_fast_len(sz) for sz in N_pad_raw] # Create pad and crop operator crop_start = [(N_pad[dim] - N[dim]) // 2 for dim in range(len(N))] else: if type(pad_size) not in (list, tuple, np.ndarray): pad_size = (pad_size, pad_size) N_pad = (pad_size[0] + N[0], pad_size[1] + N[1]) crop_start = None # Create pad operator P = Pad(N, N_pad, pad_value=pad_value, pad_start=crop_start, backend=backend, dtype=dtype) # Create F.T. operator F = FourierTransform(N_pad, dtype=dtype, backend=backend, normalize=normalize, fft_backend=fft_backend, pad=False, center=center) # Optionally create FFTShift operator if not center: FFTS = FFTShift(N_pad, dtype=dtype, backend=backend) else: FFTS = Identity(N_pad, dtype=dtype, backend=backend) # Diagonalize kernel K = Diagonalize(kernel, inner_operator=F * FFTS * P, inverse_regularizer=inverse_regularizer, label=label) # Generate composite op op = P.H * F.H * K * F * P # Define inversion function def _inverse(self, x, y): # Get current kernel kernel_f = F * FFTS * P * kernel # Invert and create operator kernel_f_inv = conj(kernel_f) / (abs(kernel_f)**2 + self.inverse_regularizer) K_inverse = Diagonalize(kernel_f_inv, backend=backend, dtype=dtype, label=label) # Set output y[:] = P.H * F.H * K_inverse * F * P * x # Set inverse function op._inverse = types.MethodType(_inverse, op) else: raise ValueError( 'Convolution mode %s is not defined! Valid options are "circular" and "windowed"' % mode) # Append type to label if '_' not in label: label += '_{' + mode + '}' # Set label op.label = label # Set inverse_regularizer op.inverse_regularizer = inverse_regularizer # Set latex to be just label def repr_latex(latex_input=None): if latex_input is None: return op.label else: return op.label + ' \\times ' + latex_input op.repr_latex = repr_latex return op