def _reconstruct(self, observation, *args, **kwargs):
    if self.torch_manual_seed:
        torch.random.manual_seed(self.torch_manual_seed)
    output_depth = 1
    input_depth = 1
    device = torch.device(('cuda:0' if torch.cuda.is_available() else 'cpu'))
    self.net_input = (0.1 * torch.randn(input_depth, *self.reco_space.shape)[None].to(device))
    self.model = UNet(input_depth, output_depth, channels=self.channels[:self.scales], skip_channels=self.skip_channels[:self.scales], use_sigmoid=True, use_norm=True).to(device)
    self.optimizer = Adam(self.model.parameters(), lr=self.lr)
    y_delta = torch.tensor(np.asarray(observation), dtype=torch.float32)
    y_delta = y_delta.view(1, 1, *y_delta.shape)
    y_delta = y_delta.to(device)
    if (self.loss_function == 'mse'):
        criterion = MSELoss()
    elif (self.loss_function == 'poisson'):
        criterion = partial(poisson_loss, photons_per_pixel=self.photons_per_pixel, mu_max=self.mu_max)
    else:
        warn('Unknown loss function, falling back to MSE')
        criterion = MSELoss()
    best_loss = np.inf
    best_output = self.model(self.net_input).detach()
    for i in tqdm(range(self.iterations), desc='DIP', disable=(not self.show_pbar)):
        self.optimizer.zero_grad()
        output = self.model(self.net_input)
        loss = (criterion(self.ray_trafo_module(output), y_delta) + (self.gamma * tv_loss(output)))
        loss.backward()
        torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1)
        self.optimizer.step()
        for p in self.model.parameters():
            p.data.clamp_(MIN, MAX)
        if (loss.item() < best_loss):
            best_loss = loss.item()
            best_output = output.detach()
        if ((self.callback_func is not None) and (((i % self.callback_func_interval) == 0) or (i == (self.iterations - 1)))):
            self.callback_func(iteration=i, reconstruction=best_output[(0, 0, ...)].cpu().numpy(), loss=best_loss)
        if (self.callback is not None):
            self.callback(self.reco_space.element(best_output[(0, 0, ...)].cpu().numpy()))
    return self.reco_space.element(best_output[(0, 0, ...)].cpu().numpy())
def _reconstruct(self, observation, *args, **kwargs):
    if self.torch_manual_seed:
        torch.random.manual_seed(self.torch_manual_seed)
    output_depth = 1
    input_depth = 1
    device = torch.device(('cuda:0' if torch.cuda.is_available() else 'cpu'))
    self.net_input = (0.1 * torch.randn(input_depth, *self.reco_space.shape)[None].to(device))
    self.model = UNet(input_depth, output_depth, channels=self.channels[:self.scales], skip_channels=self.skip_channels[:self.scales], use_sigmoid=True, use_norm=True).to(device)
    self.optimizer = Adam(self.model.parameters(), lr=self.lr)
    y_delta = torch.tensor(np.asarray(observation), dtype=torch.float32)
    y_delta = y_delta.view(1, 1, *y_delta.shape)
    y_delta = y_delta.to(device)
    if (self.loss_function == 'mse'):
        criterion = MSELoss()
    elif (self.loss_function == 'poisson'):
        criterion = partial(poisson_loss, photons_per_pixel=self.photons_per_pixel, mu_max=self.mu_max)
    else:
        warn('Unknown loss function, falling back to MSE')
        criterion = MSELoss()
    best_loss = np.inf
    best_output = self.model(self.net_input).detach()
    for i in tqdm(range(self.iterations), desc='DIP', disable=(not self.show_pbar)):
        self.optimizer.zero_grad()
        output = self.model(self.net_input)
        loss = (criterion(self.ray_trafo_module(output), y_delta) + (self.gamma * tv_loss(output)))
        loss.backward()
        torch.nn.utils.clip_grad_norm_(self.model.parameters(), max_norm=1)
        self.optimizer.step()
        for p in self.model.parameters():
            p.data.clamp_(MIN, MAX)
        if (loss.item() < best_loss):
            best_loss = loss.item()
            best_output = output.detach()
        if ((self.callback_func is not None) and (((i % self.callback_func_interval) == 0) or (i == (self.iterations - 1)))):
            self.callback_func(iteration=i, reconstruction=best_output[(0, 0, ...)].cpu().numpy(), loss=best_loss)
        if (self.callback is not None):
            self.callback(self.reco_space.element(best_output[(0, 0, ...)].cpu().numpy()))
    return self.reco_space.element(best_output[(0, 0, ...)].cpu().numpy())