def pad_undo(self, x, mode='reflect'): raw, undo_pad = pad(x, self.padding_fac, mode=mode) return raw, undo_pad
def pad(self, x): raw, _ = pad(x, self.padding_fac, mode='constant') return raw
def pad(raw, fac): raw, _ = pad(raw, fac, mode=MultiscaleBlueprint.get_padding_mode()) return raw
def encode(self, img, pout): """ Encode image to disk at path `p`. :param img: uint8 tensor of shape CHW or 1CHW :param pout: path :return actual_bpsp """ assert not os.path.isfile(pout) if len(img.shape) == 3: img = img.unsqueeze(0) # 1CHW assert len( img.shape ) == 4 and img.shape[0] == 1 and img.shape[1] == 3, img.shape assert img.dtype == torch.int64, img.dtype if auto_crop.needs_crop(img): print('Need to encode individual crops!') c = auto_crop.CropLossCombinator() for i, img_crop in enumerate(auto_crop.iter_crops(img)): bpsp_crop = self.encode( img_crop, pout + part_suffix_helper.make_part_suffix(i)) c.add(bpsp_crop, np.prod(img_crop.shape[-2:])) return c.get_bpsp() # TODO: Note that recursive is not supported. padding = 2**self.blueprint.net.config_ms.num_scales _, _, H, W = img.shape if H % padding != 0 or W % padding != 0: print( f'*** INFO: image shape ({H}X{W}) not divisible by {padding}, will pad.' ) img, padding_tuple = pad.pad( img, fac=padding, mode=MultiscaleBlueprint.get_padding_mode()) else: padding_tuple = (0, 0, 0, 0) img = img.float() with self.times.run('[-] encode forwardpass'): out = self.blueprint.net(img) if self.compare_with_theory: with self.times.run('[-] get loss'): loss_out = self.blueprint.get_loss(out) self.blueprint.net.zero_grad() entropy_coding_bytes = [] # bytes used by different scales with open(pout, 'wb') as fout: write_padding_tuple(padding_tuple, fout) for scale, dmll, uniform in self.iter_scale_dmll(): with self.times.prefix_scope(f'[{scale}]'): if uniform: entropy_coding_bytes.append( self.encode_uniform(dmll, out.S[scale], fout)) else: entropy_coding_bytes.append( self.encode_scale(scale, dmll, out, img, fout)) fout.write(_MAGIC_VALUE_SEP) num_subpixels = np.prod(img.shape) actual_num_bytes = os.path.getsize(pout) actual_bpsp = actual_num_bytes * 8 / num_subpixels if self.compare_with_theory: assumed_bpsps = [ b * 8 / num_subpixels for b in entropy_coding_bytes ] tostr = lambda l: ' | '.join(map('{:.3f}'.format, l) ) + f' => {sum(l):.3f}' overhead = (sum(assumed_bpsps) / sum(loss_out.nonrecursive_bpsps) - 1) * 100 info = f'Bitrates:\n' \ f'theory: {tostr(loss_out.nonrecursive_bpsps)}\n' \ f'assumed: {tostr(list(reversed(assumed_bpsps)))} [{overhead:.2f}%]\n' \ f'actual: => {actual_bpsp:.3f} [{actual_num_bytes} bytes]' print(info) return actual_bpsp else: return actual_bpsp
def pad(raw, fac): raw, _ = pad(raw, fac, mode='constant') return raw