def encode_file(self): """ :return: """ # Open yuv file self.__yuv_file = YuvDecoder(self.__input_file_path) # Open output file-object self.__output_file_stream = BitStream(self.__output_file_path, OpenMode.WRITE) # We want padding with zeros because of Golomb self.__output_file_stream.set_padding_mode(True) print('Encoded file:', self.__output_file_path) # Write some header information self.__output_file_stream.write_int(self.__yuv_file.frame_width, 4) self.__output_file_stream.write_int(self.__yuv_file.frame_height, 4) type_dict = {'4:4:4': 0, '4:2:2': 1, '4:2:0': 2} self.__output_file_stream.write_int( type_dict[self.__yuv_file.color_space], 4) self.__output_file_stream.write_int(self.__yuv_file.number_of_frames, 4) self.__output_file_stream.write_int(len(self.__yuv_file.raw_header), 4) self.__output_file_stream.write_int(self.__m_param, 4) # Start of actual content. Write unmodified original header to encoded file. self.__output_file_stream.write_bytes(self.__yuv_file.raw_header) counter = 1 set_ref_time() while True: self.__logger.info( f'Processing frame: #{counter} of {self.__yuv_file.number_of_frames}' ) y, u, v, ret = self.__yuv_file.read_frame() if not ret: break get_delta() self.encode_frame(y, u, v) counter += 1 original_file_size = os.stat(self.__input_file_path).st_size encoded_file_size = os.stat(self.__output_file_path).st_size print('Compression ratio: ', round(encoded_file_size / original_file_size * 100.0, 2)) # DO NOT FORGET TO CLOSE. Otherwise the last byte might not get written. self.__output_file_stream.close()
def __init__(self, input_file_path, output_file_path): """ :param input_file_path: :param output_file_path: """ # Open file handlers if input_file_path != None: self.__input_file = BitStream(input_file_path, OpenMode.READ) self.__output_file = BitStream(output_file_path, OpenMode.WRITE) if input_file_path != None: self.__input_file_size = os.stat(input_file_path).st_size
def test_read_one_bit(): output_file_path = 'write_n_bits' file = BitStream(output_file_path, OpenMode.READ) file.close() bit = file.read_bit() bit_sequence = [] while bit != -1: bit = file.read_bit() bit_sequence.append(bit)
def test_write_n_bits(): bits = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1] output_file_path = 'write_n_bits' file = BitStream(output_file_path, OpenMode.WRITE) file.write_n_bits(bits) file.close() file_read = open(output_file_path, 'rb') if hashlib.md5(file_read.read()).hexdigest( ) == '284f75f95c70ca126a2ab1b7ee63d7b4': print('write_bit: Passed!') else: print('write_bit: FAILED!') file_read.close()
def test_write_bit(): """ Tests BitStream.write_bit(). BitStream.Flush() is also tested as we are writing less than 8 bits :return: """ output_file_path = 'write_bit_test' file = BitStream(output_file_path, OpenMode.WRITE) file.write_bit(1) file.close() file_read = open(output_file_path, 'rb') if hashlib.md5(file_read.read()).hexdigest( ) == '8d39dd7eef115ea6975446ef4082951f': print('write_bit: Passed!') else: print('write_bit: FAILED!') file_read.close()
class GolombEncoder(object): """ Implements the Golomb encoder """ __input_file = None __output_file = None __input_file_size = -1 def __init__(self, input_file_path, output_file_path): """ :param input_file_path: :param output_file_path: """ # Open file handlers if input_file_path != None: self.__input_file = BitStream(input_file_path, OpenMode.READ) self.__output_file = BitStream(output_file_path, OpenMode.WRITE) if input_file_path != None: self.__input_file_size = os.stat(input_file_path).st_size def close(self): """ :return: """ # Open file handlers if self.__input_file != None: self.__input_file.close() self.__output_file.close() def write_header(self, m_value): self.__output_file.write_int(m_value, 4) def encode(self, m, report_progress_callback=None): """ :return: """ self.__output_file.set_padding_mode(False) # Write header to output file. TODO: write header specification # self.__output_file.write_int(m, 4) self.write_header(m) print(m) i = self.__input_file.read_int(1) counter = 0.0 while i != None: if report_progress_callback: report_progress_callback(counter / self.__input_file_size * 100.0) # ... self.golomb_encode(i, m) i = self.__input_file.read_int(1) counter = counter + 1 self.__input_file.close() self.__output_file.close() def golomb_encode(self, input, m): #print('Before:', input) if input > 0: input = input * 2 elif input < 0: input = (input * -2) - 1 #print('After:', input) b = int(math.ceil(math.log(m, 2))) # calculation of quotient q = int(math.floor(input / m)) # calculation of r = input - q * m # Calculate the fist bits with the use o q parameter, with unitary code # example: q = 3 -> first = 1110 str_repr = '' for i in range(q): self.__output_file.write_bit(1) str_repr += '1' self.__output_file.write_bit(0) str_repr += '0' encode = int(math.pow(2, b) - m) # Caso o valor de r seja menor que (2^b)-m vamos usar b-1 bits para representar esses valores if (r < encode): using_bits = b - 1 binary_representation_with_fixed_len = ("{0:0" + str(using_bits) + "b}").format(r) #print('BIN REPR:', str_repr + binary_representation_with_fixed_len) for c in binary_representation_with_fixed_len: self.__output_file.write_bit(int(c)) # Caso o contrario utiliza-se b bits de r+(2^b)-m para representar os restantes else: using_bits = b x = int(r + math.pow(2, b) - m) binary_representation_with_fixed_len = ("{0:0" + str(using_bits) + "b}").format(x) #print('BIN REPR:', str_repr + binary_representation_with_fixed_len) for c in binary_representation_with_fixed_len: self.__output_file.write_bit(int(c)) def set_padding_mode(self, with_zeros=True): self.__output_file.set_padding_mode(with_zeros) def decode(self, report_progress_callback=None, returnList=False): """ :return: """ # Open file handlers self.__output_file.set_padding_mode(True) m = self.__input_file.read_int(4) b = math.ceil(math.log(m, 2)) # ceil ( log2 (m) ) decode = int(math.pow(2, b) - m) counter = 0.0 rtn_list = [] while True: num_of_ones = 0 if report_progress_callback: report_progress_callback(counter / self.__input_file_size * 100.0) bit = self.__input_file.read_bit() while bit == 1: num_of_ones = num_of_ones + 1 bit = self.__input_file.read_bit() # If this happened, we have reached the end of the stream without a unary terminating 0 if bit == -1: break num_of_bits_to_read = b - 1 x = self.__input_file.read_n_bits(num_of_bits_to_read) int_x = int(x, 2) result = -1 if int_x < decode: result = num_of_ones * m + int_x else: int_x = int_x * 2 + self.__input_file.read_bit() result = num_of_ones * m + int_x - decode if result % 2 == 0: #Positive result = int(result / 2) else: result = int((result + 1) / 2 * -1) # print(result) if not returnList: self.__output_file.write_int(result, 1) else: rtn_list.append(result) counter += 1 self.__input_file.close() self.__output_file.close() if returnList: return rtn_list
class JpegLs(object): __output_file = None __m_param = 128 __pixels_out = open('out_pixels.txt', 'w') def __init__(self, input_file_path): """ Initializes a JPEG-LS encoder object """ self.__input_file_path = input_file_path self.__output_file_path = input_file_path + "_" + args.action log_fmt_string = '[%(asctime)s]{}[%(levelname)s]{} (%(module)s): %(message)s'.format( Fore.BLUE, Fore.RESET) logging.basicConfig(format=log_fmt_string, datefmt='%H:%M:%S', level=logging.DEBUG) self.__logger = logging.getLogger(__name__) self.__logger.setLevel(logging.DEBUG) self.__logger.debug('Initialized JPEG-LS codec with {}'.format( self.__input_file_path)) np.seterr(over='ignore') return def encode_file(self): """ :return: """ # Open yuv file self.__yuv_file = YuvDecoder(self.__input_file_path) # Open output file-object self.__output_file_stream = BitStream(self.__output_file_path, OpenMode.WRITE) # We want padding with zeros because of Golomb self.__output_file_stream.set_padding_mode(True) print('Encoded file:', self.__output_file_path) # Write some header information self.__output_file_stream.write_int(self.__yuv_file.frame_width, 4) self.__output_file_stream.write_int(self.__yuv_file.frame_height, 4) type_dict = {'4:4:4': 0, '4:2:2': 1, '4:2:0': 2} self.__output_file_stream.write_int( type_dict[self.__yuv_file.color_space], 4) self.__output_file_stream.write_int(self.__yuv_file.number_of_frames, 4) self.__output_file_stream.write_int(len(self.__yuv_file.raw_header), 4) self.__output_file_stream.write_int(self.__m_param, 4) # Start of actual content. Write unmodified original header to encoded file. self.__output_file_stream.write_bytes(self.__yuv_file.raw_header) counter = 1 set_ref_time() while True: self.__logger.info( f'Processing frame: #{counter} of {self.__yuv_file.number_of_frames}' ) y, u, v, ret = self.__yuv_file.read_frame() if not ret: break get_delta() self.encode_frame(y, u, v) counter += 1 original_file_size = os.stat(self.__input_file_path).st_size encoded_file_size = os.stat(self.__output_file_path).st_size print('Compression ratio: ', round(encoded_file_size / original_file_size * 100.0, 2)) # DO NOT FORGET TO CLOSE. Otherwise the last byte might not get written. self.__output_file_stream.close() def encode_frame(self, y_plane, u_plane, v_plane): """ :param y_plane: :param u_plane: :param v_plane: :return: """ avg_y = np.average(y_plane) avg_u = np.average(u_plane) avg_v = np.average(v_plane) # self.__logger.debug('Rel time: ' + str(get_delta())) get_delta() # start = time.time() # self.__logger.debug(f'Avg Y: {avg_y} || U: {avg_u} || V {avg_u}') # self.__m_param = math.floor(math.log(avg_y, 2)) # self.__output_file.write_int(self.__m_param) self.encode_plane(y_plane) # self.__logger.debug('Y plane processing time: {}'.format(round(time.time() - start, 2))) # self.__m_param = math.floor(math.log(avg_u, 2)) # self.__output_file.write_int(self.__m_param) self.encode_plane(u_plane) # self.__m_param = math.floor(math.log(avg_v, 2)) # self.__output_file.write_int(self.__m_param) self.encode_plane(v_plane) def encode_plane(self, plane): """ :param plane: :return: """ row_times = [] # A - left pixel, B - top pixel, C - top left pixel, D - top right for row in range(0, plane.shape[0]): for col in range(0, plane.shape[1]): x = get_pixel_value(plane, row, col) A = get_pixel_value(plane, row, col - 1) B = get_pixel_value(plane, row - 1, col) C = get_pixel_value(plane, row - 1, col - 1) D = get_pixel_value(plane, row - 1, col + 1) pred_x = -1 if C >= max(A, B): pred_x = min(A, B) elif C <= min(A, B): pred_x = max(A, B) else: pred_x = A + B - C residual = x - pred_x # self.__residual_txt_file.write(str(residual) + ' ') # self.__output_golomb_obj.encode_value(residual, self.__m_param) encoded_val = golomb.encode(residual, self.__m_param) self.__output_file_stream.write_n_bits(encoded_val) # self.__residual_txt_file.write('\n') # self.__logger.debug('Average row processing time:{}'.format(round(sum(row_times) / len(row_times) * 100, 2), " Len:", len(row_times))) def decode_file(self): # width = 352 # height = 288 # color_space = '4:2:0' self.__input_file_stream = BitStream(self.__input_file_path, OpenMode.READ) self.__output_file_stream = BitStream(self.__output_file_path, OpenMode.WRITE) # golomb_obj = GolombEncoder(self.__input_file_stream, # self.__output_file_stream) self.frame_width = self.__input_file_stream.read_int(4) self.frame_height = self.__input_file_stream.read_int(4) self.color_space_int = self.__input_file_stream.read_int(4) self.frame_count = self.__input_file_stream.read_int(4) # This header is the unprocessed, raw YUV header self.size_of_header = self.__input_file_stream.read_int(4) self.m_param = self.__input_file_stream.read_int(4) self.header = self.__input_file_stream.read_bytes(self.size_of_header) # WRite header to output file self.__output_file_stream.write_bytes(bytes(self.header)) # print('HEADER: ', bytes(header).decode('utf-8')) type_dict = {0: '4:4:4', 1: '4:2:2', 2: '4:2:0'} self.color_space = type_dict[self.color_space_int] self.__logger.info(f'Frame Width:\t{self.frame_width}') self.__logger.info(f'Frame Height:\t{self.frame_height}') self.__logger.info(f'Color Space:\t{self.color_space}') self.__logger.info(f'# of Frames:\t{self.frame_count}') self.__logger.info( f'Decoding. Output file:\t{self.__output_file_path}') # Calculate expected number of golomb values expected_values_per_frame = 0 uv_planes_rows = self.frame_height uv_planes_cols = self.frame_width if self.color_space_int == 0: expected_values_per_frame = self.frame_width * self.frame_height * 3 # Do not change uv_planes_rows and counts since there is no downsampling elif self.color_space_int == 1: expected_values_per_frame = self.frame_width * self.frame_height * 2 uv_planes_cols = int(uv_planes_cols / 2) elif self.color_space_int == 2: expected_values_per_frame = self.frame_width * self.frame_height * 3 / 2 uv_planes_cols = int(uv_planes_cols / 2) uv_planes_rows = int(uv_planes_rows / 2) self.__logger.debug( 'Values per frame: {}'.format(expected_values_per_frame)) self.__logger.debug( f'UV components size: {uv_planes_rows}x{uv_planes_cols}: {uv_planes_cols * uv_planes_rows} samples' ) for i in range(1, self.frame_count + 1): self.__logger.info( f'Processing frame number {i} of {self.frame_count} ') self.__output_file_stream.write_str('FRAME\n') golomb_values = golomb.decode(self.m_param, self.frame_width * self.frame_height, self.__input_file_stream) # Y plane self.decode_plane(self.frame_height, self.frame_width, golomb_values) # U plane golomb_values = golomb.decode(self.m_param, uv_planes_cols * uv_planes_rows, self.__input_file_stream) self.decode_plane(uv_planes_rows, uv_planes_cols, golomb_values) # V plane golomb_values = golomb.decode(self.m_param, uv_planes_cols * uv_planes_rows, self.__input_file_stream) self.decode_plane(uv_planes_rows, uv_planes_cols, golomb_values) self.__logger.info('Processed all frames.') # with open('check_residuals.txt', 'w') as f: # for t in golomb_values: # f.write(str(t) + ' ') self.__pixels_out.close() def decode_plane(self, row_count, col_count, plane_residuals): residuals_plane = np.asarray(plane_residuals, dtype=np.uint8) residuals_plane.shape = (row_count, col_count) decoded_frame = np.zeros((row_count, col_count), dtype=np.uint8) for row in range(0, row_count): for col in range(0, col_count): residual = get_pixel_value(residuals_plane, row, col) A = get_pixel_value(decoded_frame, row, col - 1) B = get_pixel_value(decoded_frame, row - 1, col) C = get_pixel_value(decoded_frame, row - 1, col - 1) D = get_pixel_value(decoded_frame, row - 1, col + 1) pred_x = -1 if C >= max(A, B): pred_x = min(A, B) elif C <= min(A, B): pred_x = max(A, B) else: pred_x = A + B - C pixel_value = residual + pred_x decoded_frame[row, col] = pixel_value self.__output_file_stream.write_int(pixel_value.item(), 1)
def decode_file(self): # width = 352 # height = 288 # color_space = '4:2:0' self.__input_file_stream = BitStream(self.__input_file_path, OpenMode.READ) self.__output_file_stream = BitStream(self.__output_file_path, OpenMode.WRITE) # golomb_obj = GolombEncoder(self.__input_file_stream, # self.__output_file_stream) self.frame_width = self.__input_file_stream.read_int(4) self.frame_height = self.__input_file_stream.read_int(4) self.color_space_int = self.__input_file_stream.read_int(4) self.frame_count = self.__input_file_stream.read_int(4) # This header is the unprocessed, raw YUV header self.size_of_header = self.__input_file_stream.read_int(4) self.m_param = self.__input_file_stream.read_int(4) self.header = self.__input_file_stream.read_bytes(self.size_of_header) # WRite header to output file self.__output_file_stream.write_bytes(bytes(self.header)) # print('HEADER: ', bytes(header).decode('utf-8')) type_dict = {0: '4:4:4', 1: '4:2:2', 2: '4:2:0'} self.color_space = type_dict[self.color_space_int] self.__logger.info(f'Frame Width:\t{self.frame_width}') self.__logger.info(f'Frame Height:\t{self.frame_height}') self.__logger.info(f'Color Space:\t{self.color_space}') self.__logger.info(f'# of Frames:\t{self.frame_count}') self.__logger.info( f'Decoding. Output file:\t{self.__output_file_path}') # Calculate expected number of golomb values expected_values_per_frame = 0 uv_planes_rows = self.frame_height uv_planes_cols = self.frame_width if self.color_space_int == 0: expected_values_per_frame = self.frame_width * self.frame_height * 3 # Do not change uv_planes_rows and counts since there is no downsampling elif self.color_space_int == 1: expected_values_per_frame = self.frame_width * self.frame_height * 2 uv_planes_cols = int(uv_planes_cols / 2) elif self.color_space_int == 2: expected_values_per_frame = self.frame_width * self.frame_height * 3 / 2 uv_planes_cols = int(uv_planes_cols / 2) uv_planes_rows = int(uv_planes_rows / 2) self.__logger.debug( 'Values per frame: {}'.format(expected_values_per_frame)) self.__logger.debug( f'UV components size: {uv_planes_rows}x{uv_planes_cols}: {uv_planes_cols * uv_planes_rows} samples' ) for i in range(1, self.frame_count + 1): self.__logger.info( f'Processing frame number {i} of {self.frame_count} ') self.__output_file_stream.write_str('FRAME\n') golomb_values = golomb.decode(self.m_param, self.frame_width * self.frame_height, self.__input_file_stream) # Y plane self.decode_plane(self.frame_height, self.frame_width, golomb_values) # U plane golomb_values = golomb.decode(self.m_param, uv_planes_cols * uv_planes_rows, self.__input_file_stream) self.decode_plane(uv_planes_rows, uv_planes_cols, golomb_values) # V plane golomb_values = golomb.decode(self.m_param, uv_planes_cols * uv_planes_rows, self.__input_file_stream) self.decode_plane(uv_planes_rows, uv_planes_cols, golomb_values) self.__logger.info('Processed all frames.') # with open('check_residuals.txt', 'w') as f: # for t in golomb_values: # f.write(str(t) + ' ') self.__pixels_out.close()
def test_write_14_bits(): """ It aims to test the padding when the buffer is not full and there already is one byte written. :return: """ output_file_path = 'write_14_bit_test' file = BitStream(output_file_path, OpenMode.WRITE) file.write_bit(1) file.write_bit(1) file.write_bit(1) file.write_bit(1) file.write_bit(1) file.write_bit(0) file.write_bit(1) file.write_bit(1) file.write_bit(0) file.write_bit(1) file.write_bit(1) file.write_bit(1) file.write_bit(1) file.write_bit(1) # 14 calls to write_bit file.close() file_read = open(output_file_path, 'rb') if hashlib.md5(file_read.read()).hexdigest( ) == 'df349ae692f79d86f3aeed3a42547576': print('write_14_bit: Passed!') else: print('write_14_bit: FAILED!')