PHP结合FFmpeg处理图像嵌入视频后颜色值偏移问题求助
问题:视频帧提取后颜色偏移的原因与解决方案
我用PHP给图像写入特定颜色,保存为无损PNG/BMP后,用FFmpeg转成带音频的MP4。但从视频提取帧读取颜色时,出现明显偏移——嵌入的颜色值为44743072,提取后变成4539914、4474121、4408072等(x从0到23,y=0)。无论使用PNG还是BMP格式,都会出现像素颜色丢失的情况。
生成视频的代码
$canvas = imagecreatefromjpeg('translate/first_frame.jpg'); // create a random color $rand = str_pad(dechex(rand(0x000000, 0xFFFFFF)), 6, 0, STR_PAD_LEFT); $dec_color= hexdec($rand); // add the new color to image for ($i=0; $i < 24; $i++) { imagesetpixel($canvas,$i,0,$dec_color); } // store the image and close the file opened // $filename = 'translate/test/output.png'; $filename = 'translate/test/output.bmp'; // imagepng($canvas, $filename); imagebmp($canvas, $filename); imagedestroy($canvas); $frame = $filename; // an image(png,gif,etc) $audio = 'translate/output/audio/abcdefghijklmnopqrstuvwxya.mp3'; $output = 'translate/output/video/'.time().'.mp4'; $cmd = 'ffmpeg -loop 1 -y -i '.$frame.' -i '.$audio.' -c:v libx264 -tune stillimage -c:a copy -shortest '.$output; shell_exec($cmd);
提取帧并读取颜色的代码
if($request->hasFile('video')){ $file = $request->file('video'); $filename = $file->getClientOriginalName(); $path = public_path('translate/test/'); }else{ return 'No file uploaded'; } if ($file->move($path, $filename)) { $video = 'translate/test/'.$filename; }else{ return 'error file upload'; } // $output = 'translate/output/image/'.time().'.png'; $output = 'translate/output/image/'.time().'.bmp'; // $output = 'translate/output/image/'.time().'.jpg'; $cmd = 'ffmpeg -i '.$video.' -vframes 1 '.$output; shell_exec($cmd); // $dimg = imagecreatefrompng($output); $dimg = imageCreateFromBmp($output); // $dimg = imagecreatefromjpeg($output); $extracted_color = array(); for ($x=0; $x < 24 ; $x++) { $extracted_color[]= imagecolorat($dimg, $x, 0); } echo "<br>Retrived colors:<pre>".print_r($extracted_color,1)."</pre>"; imagedestroy($dimg);
原因分析与解决方案
核心原因
- libx264默认是有损编码:即使添加了
-tune stillimage,默认的CRF值(质量参数)会进行有损压缩,导致像素颜色精度丢失。 - YUV颜色空间转换损失:H.264默认使用YUV420采样格式,这种格式会对色度通道进行下采样,导致颜色细节丢失。
- JPEG画布的颜色空间误差:原代码从JPEG读取画布,JPEG本身是有损压缩且基于YUV空间,转RGB时可能引入精度偏差。
具体修复步骤
修改FFmpeg编码参数,强制无损:
生成视频的命令添加-crf 0(开启H.264无损模式)和-pix_fmt yuv444p(全采样YUV,避免色度下采样):$cmd = 'ffmpeg -loop 1 -y -i '.$frame.' -i '.$audio.' -c:v libx264 -crf 0 -pix_fmt yuv444p -tune stillimage -c:a copy -shortest '.$output;-crf 0会让视频文件变大,但能完全保留原始图像的像素信息;-pix_fmt yuv444p确保每个像素的色度信息都被完整存储。改用RGB画布创建图像:
避免从JPEG读取带来的颜色空间转换误差,直接创建真彩色RGB画布并复制原JPEG内容:// 替换原imagecreatefromjpeg的初始化代码 $src_img = imagecreatefromjpeg('translate/first_frame.jpg'); $width = imagesx($src_img); $height = imagesy($src_img); $canvas = imagecreatetruecolor($width, $height); // 复制原图像到RGB画布 imagecopy($canvas, $src_img, 0, 0, 0, 0, $width, $height); imagedestroy($src_img);优化提取帧的FFmpeg命令:
提取帧时指定RGB像素格式,避免FFmpeg自动转换带来的损失:$cmd = 'ffmpeg -i '.$video.' -vframes 1 -pix_fmt rgb24 '.$output;
内容的提问来源于stack exchange,提问作者Jeenus Junanio
相关产品推荐
相关产品推荐

