通过修改 AC 系数将 jpeg 块变为黑色

colour jpeg block black by modifying AC coefficient

提问人:carandraug 提问时间:9/28/2023 更新时间:10/2/2023 访问量:19

问:

我试图通过操作 libjpeg 上的各个 DCT 系数值,在 DCT 空间上将 JPEG 图像的区域着色为黑色。

我知道 DC 系数指定了色调。因此,忽略上/下采样问题,应该能够通过将所有交流系数归零并操纵直流系数来为整个模块着色。但是,我无法弄清楚值的范围是多少。

例如,要用中点灰色为左上角的块着色,可以:

int row = 0;
int col = 0;

jvirt_barray_ptr *src_coef_arrays = jpeg_read_coefficients(&src.info);
for (int ci = 0; ci < src.info.num_components; ci++) {

    JBLOCKARRAY buffer = src.info.mem->access_virt_barray(
        (j_common_ptr)&src.info,
        src_coef_arrays[ci],
        row,
        comp_info.v_samp_factor,
        TRUE
    );
    for (int k = 0; k < DCTSIZE2; k++)
        blk_buf[0][col][k] = 0;
}

假设 YCbCr,而不是中点灰色,只需改变亮度分量的 DC 系数即可变暗/变亮:

  if (ci == 0)  // luma component
      blk_buf[0][col][0] = -50;  // tweak DC coeff of luma component
  else
      blk_buf[0][col][0] = 0;    // zero DC coeff of chroma components
  for (int k = 1; k < DCTSIZE2; k++)
      blk_buf[0][col][k] = 0;    // zero all AC coeffs

但是,我还没有弄清楚如何从特定的 YCbCr 值转到 DC 值,即黑色的 DC 值是多少?以及如何从任何颜色到DC值?

jpeg libjpeg-turbo

评论


答:

0赞 carandraug 10/2/2023 #1

在 libjpeg 中操作“原始”DCT 系数有两个关键点:

  1. 返回的系数已经/仍在量化
  2. 系数按 8 倍放大

(libjpeg 的内部头文件)提到了 8 的系数:jdct.h

/*
 [...]
 * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE).
 * The DCT outputs are returned scaled up by a factor of 8; they therefore
 * have a range of +-8K for 8-bit data, +-128K for 12-bit data.  This
 [...]
 */

因此,查找给定颜色所需的直流值的过程是:

  1. 将所需的颜色转换为输出 jpeg 色彩空间(通常为 YCbCR)。检查。有效的 jpeg 色彩空间为:info.jpeg_color_space

    • JCS_YCbCr
    • JCS_GRAYSCALE
    • JCS_RGB
    • JCS_CMYK
    • JCS_YCCK
  2. 将值转换为范围乘以 8。假设您想要黑色,并且 JPEG 颜色空间是 YCbCR,因为黑色是(假设 YCbCr 表示法的值介于 -128 和 127 之间),那么:-CENTERJSAMPLE : (CENTERJSAMPLE-1)YCbCr(-128, 0, 0)

    int DC_black_Y = -CENTERJSAMPLE *8;
    int DC_black_Cb = 0;
    int DC_black_Cr = 0;
    
  3. 将该值除以该图像组件的量化器矩阵的第一个元素(DC 值的量化器值)。您可以获取每个组件使用的量化器值:

    for (int ci = 0; ci < info.num_components; ci++) {
      const int quant_tbl_no = info.comp_info[ci].quant_tbl_no;
      const UINT16 dc_quant = info.quant_tbl_ptrs[quant_tbl_no]->quantval[0];
      [...]
    
  4. 将值从零四舍五入(如果为正,则为下限)

    // Round towards closest infinity (away from zero)
    // assuming dc_value and dc_quant are integers
    if (dc_value < 0)
      dc_value = (dc_value - dc_quant +1) / dc_quant;
    else if (dc_value > 0)
      dc_value = (dc_value + dc_quant -1) / dc_quant;
    // else do nothing, it's zero