我正在用 Java 创建一个 H.261 编码器: https://github.com/ericynt/h26x-playground
为了制作编码器,我使用了 H.261 标准文档: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.261-198811-S!!PDF-E&type=items
应用程序的结构是: FrameGenerator --[RGB 帧]--> H261Encoder --[H261 数据包]--> UdpStreamer --[UDP 数据报]--> 127.0.0.1:55555
预期的编码器输出应与输入相同,但压缩算法的质量损失较小。
看起来宏块位于帧中的正确位置,但不知何故它们没有正确显示。我找不到问题所在。据我所知,我遵循标准,但我一定错过了一些东西..
如果我必须猜测哪里出了问题,我会在 Y 组件部分的 toBlocks 方法中说(但我可能是错的):
private int[][][] toBlocks (
final Pair<Integer, Integer> pixelRowAndColumn,
final int[][][] yCbCr
) {
// YCbCr 4:4:4 to 4:2:0
// and transform 16x16x3 to 6x8x8 (4 Y, 1 Cb, 1 Cr)
int[][][] blocks = new int[TOTAL_BLOCKS][BLOCK_SIZE][BLOCK_SIZE];
int[][] cbAccumulators = new int[BLOCK_SIZE][BLOCK_SIZE];
int[][] crAccumulators = new int[BLOCK_SIZE][BLOCK_SIZE];
int[][] countAccumulators = new int[BLOCK_SIZE][BLOCK_SIZE];
for (int i = pixelRowAndColumn.getKey(); i < pixelRowAndColumn.getKey() + MACROBLOCK_SIZE; i++) {
for (int j = pixelRowAndColumn.getValue(); j < pixelRowAndColumn.getValue() + MACROBLOCK_SIZE; j++) {
int x = i % 16;
int y = j % 16;
// Y component
if (x < 8 && y < 8) {
// Bottom left
blocks[2][x][y] = yCbCr[i][j][0];
} else if (x < 8) {
// Top left
blocks[0][x][y - 8] = yCbCr[i][j][0];
} else if (y < 8) {
// Bottom right
blocks[3][x - 8][y] = yCbCr[i][j][0];
} else {
// Top right
blocks[1][x - 8][y - 8] = yCbCr[i][j][0];
}
// Cb and Cr components
int cbCrX = x / 2;
int cbCrY = y / 2;
cbAccumulators[cbCrX][cbCrY] += yCbCr[i][j][1];
crAccumulators[cbCrX][cbCrY] += yCbCr[i][j][2];
countAccumulators[cbCrX][cbCrY]++;
}
}
// Calculate and assign the averages for Cb and Cr
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
blocks[4][i][j] = cbAccumulators[i][j] / countAccumulators[i][j];
blocks[5][i][j] = crAccumulators[i][j] / countAccumulators[i][j];
}
}
return blocks;
}
我的问题是:输出中出现奇怪模式的原因是什么以及如何解决它?
在寻找问题原因几个小时后,我终于找到了问题所在。
在查看 C 语言中 H.261 编码器的实现时,我发现了一种转置方法。据我所知,标准文档中没有提及这一点。 https://github.com/maikmerten/p64/blob/cfba372679f6683a45c95817d9ed51c09566ce31/transform.c#L38
添加转置矩阵并垂直翻转 Y 分量块的方法后,问题就解决了!
现在,帧看起来与输入相同,除了压缩算法产生的一些轻微伪像之外,如预期的那样