YCbCr转RGB图像损坏排查及解决:算术溢出/下溢修复
YCbCr转RGB图像损坏问题:解决记录与原问题
更新:问题已解决。我的YCbCr转RGB函数未先检查算术溢出与下溢问题。感谢所有回复者!我将保留问题代码并附上解决方案,以便其他开发者参考。
int willOverflow(int16_t result) { return result > 255; } int willUnderflow(int16_t result) { return result < 0; } void yCbCrToRgb(uint8_t* yCbCr, uint8_t* targetRgb) { uint8_t luma = yCbCr[0]; uint8_t Cb = yCbCr[1]; uint8_t Cr = yCbCr[2]; if (!willOverflow(luma + (1.402 * (Cr - 128))) && !willUnderflow(luma + (1.402 * (Cr - 128)))) targetRgb[0] = luma + (1.402 * (Cr - 128)); else targetRgb[0] = willUnderflow(luma + (1.402 * (Cr - 128))) ? 0 : 255; if (!willOverflow(luma + (-0.344136 * (Cb - 128)) + (-0.714136 * (Cr - 128))) && !willUnderflow(luma + (-0.344136 * (Cb - 128)) + (-0.714136 * (Cr - 128)))) targetRgb[1] = luma + (-0.344136 * (Cb - 128)) + (-0.714136 * (Cr - 128)); else targetRgb[1] = willUnderflow(luma + (-0.344136 * (Cb - 128)) + (-0.714136 * (Cr - 128))) ? 0 : 255; if (!willOverflow(luma + (1.772 * (Cb - 128))) && !willUnderflow(luma + (1.772 * (Cb - 128)))) targetRgb[2] = luma + (1.772 * (Cb - 128)); else targetRgb[2] = willUnderflow(luma + (1.772 * (Cb - 128))) ? 0 : 255; }
原问题描述
我是一名C语言新手开发者,首次在Stack Overflow发帖,正在编写JPEG编码器。目前已实现将ppm文件中的原始RGB像素数据转换为YCbCr色彩空间,并按照4:2:0子采样规则对蓝色(Cb)和红色(Cr)色度通道进行子采样。处理后亮度(Y)通道的采样数与图像像素数一致,两个色度通道的采样数仅为像素数的1/4。非子采样的YCbCr转RGB过程正常,但将这三个通道重组后转RGB并写入输出文件时,出现明显的图像损坏:部分图像仅在亮区与暗区交界处存在异常颜色像素,其余区域正常;部分图像则同时存在正常区域与大面积损坏区域。在进行值中心化及离散余弦变换前,我希望确保子采样与转RGB的流程正常。
图像相关结构体定义
// 以下是我用于表示图像的结构体 typedef struct ppmHeader { uint32_t numPixels; uint16_t pixelWidth, pixelHeight; uint8_t maxColorVal; size_t headerBytes, pixelDataSize, bytesPerPixel, rowSize; uint8_t* headerStr; } ppmHeader; typedef struct YCBCR_Pixel_24 { uint8_t yCbCr[YCBCR_BYTES]; // 3字节 } YCBCR_Pixel_24; typedef struct intermediateImg { YCBCR_Pixel_24* pixelData; int16_t* lumaSample; int16_t* CbSample; int16_t* CrSample; } intermediateImg;
RGB转YCbCr实现
// 将RGB转换为YCbCr,转换及逆转换公式均来自MATLAB void setYCbCr(YCBCR_Pixel_24* pixel, uint8_t* rgbIn) { uint8_t luminance, Cb, Cr; uint8_t r, g, b; r = rgbIn[0]; g = rgbIn[1]; b = rgbIn[2]; luminance = 0.257 * r + 0.504 * g + 0.098 * b + 16; Cb = -0.148 * r - 0.291 * g + 0.439 * b + 128; Cr = 0.439 * r - 0.368 * g - 0.071 * b + 128; pixel->yCbCr[0] = luminance; pixel->yCbCr[1] = Cb; pixel->yCbCr[2] = Cr; }
核心实现代码
/* 4:2:0子采样:每2x2像素块中取1个Cb和Cr色度样本 */ void subsampleChrominance(uint16_t imgWidth, uint32_t subSampledSize, intermediateImg* img) { uint32_t ssIndex = 0; //子采样索引 uint32_t pdIndex = 0; //像素数据索引 while (ssIndex < subSampledSize) { img->CbSample[ssIndex] = img->pixelData[pdIndex].yCbCr[1]; img->CrSample[ssIndex] = img->pixelData[pdIndex].yCbCr[2]; if ((pdIndex + 2) % imgWidth == 0) { pdIndex = (pdIndex + 2) + imgWidth; } else pdIndex += 2; ssIndex++; } } /* 模拟从4:2:0子采样的色度信号重建完整YCbCr图像 */ void writeFromSignals(intermediateImg* img, ppmHeader* imgSpecs, FILE* target) { uint32_t pdIndex, ssIndex, rowCount; pdIndex = ssIndex = rowCount = 0; uint32_t samplesPerRow = imgSpecs->pixelWidth / 2; while (pdIndex < imgSpecs->numPixels) { img->pixelData[pdIndex].yCbCr[0] = img->lumaSample[pdIndex]; img->pixelData[pdIndex].yCbCr[1] = img->CbSample[ssIndex]; img->pixelData[pdIndex].yCbCr[2] = img->CrSample[ssIndex]; if ((pdIndex+1) % imgSpecs->pixelWidth == 0) { rowCount++; if (rowCount % 2 != 0) ssIndex -= (samplesPerRow-1); else ssIndex++; pdIndex++; continue; } pdIndex++; if (pdIndex % 2 == 0) ssIndex++; } writeIntermediate_rgb(img, imgSpecs, target); } /* 将YCbCr转换为RGB并写入文件 */ void writeIntermediate_rgb(intermediateImg* img, ppmHeader* specs, FILE* target) { uint32_t i = 0; uint32_t sI = 0; uint8_t rgb[BYTES_PER_PIXEL_24]; // 3字节 fwrite(specs->headerStr, 1, specs->headerBytes, target); while (i < specs->numPixels) { yCbCrToRgb(&img->pixelData[i].yCbCr[0], &rgb[0]); fwrite(&rgb[0], 1, BYTES_PER_PIXEL_24, target); i++; if (i % 2 == 0) sI++; } } /* 未处理溢出的YCbCr转RGB函数 */ void yCbCrToRgb(uint8_t* yCbCr, uint8_t* targetRgb) { uint8_t luma = yCbCr[0]; uint8_t Cb, Cr; Cb = yCbCr[1]; Cr = yCbCr[2]; targetRgb[0] = 1.164 * (luma - 16) + 1.596 * (Cr - 128); targetRgb[1] = 1.164 * (luma - 16) - 0.813 * (Cr - 128) - 0.392 * (Cb - 128); targetRgb[2] = 1.164 * (luma - 16) + 2.017 * (Cb - 128); }
程序执行流程
- 程序读取图像到内存,将每个3字节RGB像素转换为3字节YCbCr像素,存储在intermediateImg的pixelData字段,Y分量存入lumaSample字段。
- 调用subsampleChrominance填充CbSample和CrSample字段。
- 调用writeFromSignals从子采样信号重建完整图像。
问题排查情况
我已有从非子采样YCbCr重建正常图像的函数,理解子采样是有损的,但同类示例中转RGB后图像与原图几乎一致。目前已验证色度样本分配逻辑正确,YCbCr转RGB公式无误,尝试将采样数据类型从int16_t改为uint8_t无改善,怀疑writeFromSignals的指针算术存在问题,但小图测试未发现异常。
损坏图像现象
- 图像亮区出现异常青色像素,损坏程度较轻
- 图像暗区存在大量噪点,损坏程度较重
我可能忽略了某些明显问题,也不熟悉此类流程的调试方法,可提供更多代码或信息,感谢各位开发者的帮助。
内容的提问来源于stack exchange,提问作者tednickels
相关产品推荐
相关产品推荐

