JPEG2000的Kakadu源代码浅析之四:码流解码(二)

  解码过程的最关键部分由与kdu_decoder类一一对应的实体类kd_decoder对象完成。每个kd_decoder对应一个分量的一个子带(subband)。在kd_decoder构造的时候,主要工作是从字带对象band中获取关于码块的信息:
  nominal_block_size——码块大小
  first_block_size——实际(在子带中的)首码块大小
  block_indices——码块组的索引
  kdu_decoder的主要运算在虚函数实现pull中完成。而这个函数的主要工作在decode_row_of_blocks中进行。该过程对一个字带中的一行码块逐个进行解码:
  1 调用kdu_block_decoder::open_block(kdu_coords block_idx, int *)打开并初始化码块,详情在本章介绍。
  2 调用kdu_block_decoder::decode(kdu_block *)进行EBCOT块解码,其解码具体过程将在后续章节介绍;
  3 根据解码返回的num_passes信息,决定EBCOT解码系数的处置方案,如果num_passes为0,则说明系数全0,否则将系数复制到kd_decoder的缓冲区中,此后这个缓冲区内容再由pull函数复制到函数参数指定的缓冲区中,这些数据将被用于小波逆变换。

  kdu_subband::open_block(...)函数里涉及码块在JPEG2000体系中的地位的所有论题。由于码块是JPEG2000体系中最基本的单元,所以比较重要。
  open_block函数的基本流程是:
  1 根据block_idx计算出precinct_idx,precinct_idx = block_idx / <blocks_per_precinct>。
  2 在当前resolution中定位相应的precinct,如果为NULL则创建(最初均为NULL)。
  3 加载precinct。
  4 创建码块结构。
  5 初始化公共变量,即对于编码和解码都一致的初始化。
  6 获取码块的全部数据到码块缓冲区中,主要通过调用void retrieve_data(kdu_block *, int max_layers)函数完成。

  === 详述 ===

  == 结构详貌 ==
  以下所述拼接块、分量、分辨率、分区等,可以看成操作集,分别用T,C,B,P表示。T对图像Img分块,T的每个元素t为所取的分块;C对图像分色彩,C的每个元素c表示一个分量;B对上两个操作的结果ctImg进行2维子带划分(金字塔状),B的每个元素b是一个子带划分,其中每个低通划分又组成分辨率操作集R。P对T、C、R的操作结果rctImg做分区操作,而这个操作能够诱导出一个合理的P对B结果的分区操作P',这个诱导的操作是一一对应的,从而记P=P'。于是可以用笛卡尔积(Cartesian product)T*C*B*P表示整个操作序列集合,其元素tcbp作用于Img上得到的pbctImg,是一个分区。
  kd_tile:代表一个拼接块(即tile),包含一个kd_tile_comp类型的变长数组按分量存放数据,一般三色彩分量图像,该数组必然初始化为含3个单元。
  kd_tile_comp:拼接块分量。代表拼接块中的一个分量(即component),相当于拼接块集和分量集的笛卡尔积的一个元素,映射到数据实体是对应拼接块和分量数据的交。拼接块分量是JPEG2000中独立使用DWT的单元,DWT分析的结果是产生分辨率和子带。拼接块分量结构化数据主要是一个kd_resolution类型的变长数组,它包含dwt_levels+1个单元,有关分辨率(resolution)和子带(subband)已在“码流解码(一)”中叙述。
  kd_resolution:代表拼接块分量中的一个分辨率(即resolution),包含kd_subband类型和kd_precinct类型的变长数组各一个。前者存放子带数据,对于最低分辨率,只含有LL子带,则只分配1个单元,而对其他分辨率,均含三个子带,则分配4个单元,对应存放在后3个单元中。
  kd_precinct:代表分辨率中的一个分区(即precinct)。每个分区也分解为相应分辨率的子带,对应存放在一个kd_precinct_band的定长数组单元中,这些子带和该precinct对应分辨率的子带对应。
  kd_precinct_band:分区的一个子带映射,代表分辨率下子带集和分区集的笛卡尔积中的一个元素。其中包含一个变长的kd_block类型的码块数组。
  kd_block:JPEG2000体系中的最小的解码单元。

  == 步骤详述 ==
  2 创建kd_precinct对象:
  2.1 首先把kd_resolution中的指针引到自身。
  2.2 建立precinct的区域dims,这个值从resolution->precinct_partition中获得,一般为(0,0)-(0x8000,0x8000),然后和resolution本身的dims求区域交集。
  2.3 以对应的resolution为基础,遍历resolution中的所有子带,将kd_precinct_band数组单元(kd_precinct的下属)和相应子带连接(注意结构概貌中的定义)。通过子带区域和码块尺寸计算出子带所占领的码块索引。调用kd_block::build_tree(...)函数创建码块数组,先分层,以码块索引作为底层,每层的码块数是上一层的一半,从而得到总码块数和层数,分配码块数组空间,最后对各层初始化。
  2.4 ……

posted @ 2006-04-29 12:15  quanben  阅读(351)  评论(0编辑  收藏  举报