我正在尝试通过操纵 libjpeg 上的各个 DCT 系数值,将 DCT 空间上的 JPEG 图像区域着色为黑色。
我知道 DC 系数指定了色调。因此,忽略上/下采样问题,应该能够通过将所有 AC 系数归零并操纵 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 值?
在 libjpeg 中操作“原始”DCT 系数有两个关键点:
jdct.h
(libjpeg 的内部头文件)中提到了 8 的因子:
/*
[...]
* 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
[...]
*/
因此,查找给定颜色所需的 DC 值的过程是:
将所需颜色转换为输出 jpeg 颜色空间(通常为 YCbCR)。检查
info.jpeg_color_space
。有效的 jpeg 颜色空间是:
JCS_YCbCr
JCS_GRAYSCALE
JCS_RGB
JCS_CMYK
JCS_YCCK
将值转换为
-CENTERJSAMPLE : (CENTERJSAMPLE-1)
范围乘以8。假设您想要黑色,JPEG颜色空间是YCbCR,因为黑色是YCbCr(-128, 0, 0)
(假设YCbCr表示法取-128到127之间的值),那么:
int DC_black_Y = -CENTERJSAMPLE *8;
int DC_black_Cb = 0;
int DC_black_Cr = 0;
将该值除以该图像分量的量化器矩阵的第一个元素(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];
[...]
将值远离零舍入(如果为正则取整,如果为负则取下限)
// 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