如何用C++封装视频文件的H.264码流

2024-01-12

我正在尝试转换视频文件(.mp4) 到 Dicom 文件。
我通过在 Dicom 中存储单个图像(视频的每帧一个图像)成功地做到了这一点,
但结果是文件太大,这对我来说不好。
相反,我想将存储在视频文件中的 H.264 比特流封装到 Dicom 文件中。
我尝试按如下方式获取文件的字节:

std::ifstream inFile(file_name, std::ifstream::binary);

inFile.seekg(0, inFile.end);
std::streampos length = inFile.tellg();
inFile.seekg(0, inFile.beg);

std::vector<unsigned char> bytes(length);

inFile.read((char*)&bytes[0], length);

但我认为我错过了诸如封装读取字节之类的事情,因为结果 Dicom 文件是黑色图像。

在 python 中,我将使用 pydicom.encaps.encapsulate 函数来实现此目的:
https://pydicom.github.io/pydicom/dev/reference/ generated/pydicom.encaps.encapsulate.html https://pydicom.github.io/pydicom/dev/reference/generated/pydicom.encaps.encapsulate.html

with open(videofile, 'rb') as f:
    dataset.PixelData = encapsulate([f.read()])

C++ 中是否有相当于encapsulate功能?
或者任何不同的方式来获取一个流中视频的封装像素数据而不是逐帧获取?

这是初始化的代码Dcmdataset, 使用bytes提取:

VideoFileStream* vfs = new VideoFileStream();
vfs->setFilename(file_name);
if (!vfs->open())
    return false;

DcmDataset* dataset = new DcmDataset();
dataset->putAndInsertOFStringArray(DCM_SeriesInstanceUID, dcmGenerateUniqueIdentifier(new char[100], SITE_SERIES_UID_ROOT));
dataset->putAndInsertOFStringArray(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(new char[100], SITE_INSTANCE_UID_ROOT));
dataset->putAndInsertOFStringArray(DCM_StudyInstanceUID, dcmGenerateUniqueIdentifier(new char[100], SITE_STUDY_UID_ROOT));
dataset->putAndInsertOFStringArray(DCM_MediaStorageSOPInstanceUID, dcmGenerateUniqueIdentifier(new char[100], SITE_UID_ROOT));
dataset->putAndInsertString(DCM_MediaStorageSOPClassUID, UID_VideoPhotographicImageStorage);
dataset->putAndInsertString(DCM_SOPClassUID, UID_VideoPhotographicImageStorage);
dataset->putAndInsertOFStringArray(DCM_TransferSyntaxUID, UID_MPEG4HighProfileLevel4_1TransferSyntax);
dataset->putAndInsertOFStringArray(DCM_PatientID, "987655");
dataset->putAndInsertOFStringArray(DCM_StudyDate, "20050509");
dataset->putAndInsertOFStringArray(DCM_Modality, "ES");
dataset->putAndInsertOFStringArray(DCM_PhotometricInterpretation, "YBR_PARTIAL_420");
dataset->putAndInsertUint16(DCM_SamplesPerPixel, 3);
dataset->putAndInsertUint16(DCM_BitsAllocated, 8);
dataset->putAndInsertUint16(DCM_BitsStored, 8);
dataset->putAndInsertUint16(DCM_HighBit, 7);
dataset->putAndInsertUint16(DCM_Rows, vfs->height());
dataset->putAndInsertUint16(DCM_Columns, vfs->width());
dataset->putAndInsertUint16(DCM_CineRate, vfs->framerate());
dataset->putAndInsertUint16(DCM_FrameTime, 1000.0 * 1 / vfs->framerate());
const Uint16* arr = new Uint16[]{ 0x18,0x00, 0x63, 0x10 };  
dataset->putAndInsertUint16Array(DCM_FrameIncrementPointer, arr, 4);
dataset->putAndInsertString(DCM_NumberOfFrames, std::to_string(vfs->numFrames()).c_str());
dataset->putAndInsertOFStringArray(DCM_FrameOfReferenceUID, dcmGenerateUniqueIdentifier(new char[100], SITE_UID_ROOT));
dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);
dataset->putAndInsertUint16(DCM_PlanarConfiguration, 0);
dataset->putAndInsertOFStringArray(DCM_ImageType, "ORIGINAL");
dataset->putAndInsertOFStringArray(DCM_LossyImageCompression, "01");
dataset->putAndInsertOFStringArray(DCM_LossyImageCompressionMethod, "ISO_14496_10");
dataset->putAndInsertUint16(DCM_LossyImageCompressionRatio, 30);
dataset->putAndInsertUint8Array(DCM_PixelData, (const Uint8 *)bytes.data(), length);

DJ_RPLossy repParam;
dataset->chooseRepresentation(EXS_MPEG4HighProfileLevel4_1, &repParam);
dataset->updateOriginalXfer();

DcmFileFormat fileformat(dataset); 
OFCondition status = fileformat.saveFile("C://temp//videoTest", EXS_LittleEndianExplicit);

技巧是将属性 PixelData 的值重定向到文件流。这样,视频就可以按需分块加载(即当访问属性时)。 但你必须显式地创建整个结构,即:

  • 像素数据元素
  • 像素序列...
  • ...偏移表
  • ...包含 MPEG 文件内容的单个项目

Code

// set length to the size of the video file
DcmInputFileStream dcmFileStream(videofile.c_str(), 0);
DcmPixelSequence* pixelSequence = new DcmPixelSequence(DCM_PixelSequenceTag));
DcmPixelItem* offsetTable = new DcmPixelItem(DCM_PixelItemTag);
pixelSequence->insert(offsetTable);
DcmPixelItem* frame = new DcmPixelItem(DCM_PixelItemTag);
frame->createValueFromTempFile(dcmFileStream.newFactory(), OFstatic_cast(Uint32, length), EBO_LittleEndian);
pixelSequence->insert(frame);
DcmPixelData* pixelData = new DcmPixeldata(DCM_PixelData);
pixelData->putOriginalRepresentation(EXS_MPEG4HighProfileLevel4_1, nullptr, pixelSequence);
dataset->insert(pixelData, true);
DcmFileFormat fileformat(dataset); 
OFCondition status = fileformat.saveFile("C://temp//videoTest");

请注意,如果您以 VR 隐式小尾数法保存文件,则会“破坏”压缩。

如上所述,并且在代码中显而易见,整个 MPEG 文件被包装到 PixelData 中的单个项目中。这符合 DICOM 标准,但您可能希望将每个帧封装在一个项目中。

注意:此处不提供错误处理

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何用C++封装视频文件的H.264码流 的相关文章

随机推荐

  • Lua检查文件是否打开

    我正在尝试编写一个 lua 文件脚本来检查某个文件是否打开 然后我希望它关闭该文件 如果该文件已打开 我知道如何检查文件是否存在 但我需要知道如何检查文件是否打开 即文件正在运行 Lua 与 C C 和几乎所有其他语言一样 只能关闭它打开的
  • 页面加载时检查引导表复选框

    我在用引导表 http bootstrap table wenzhixin net cn documentation 我正在尝试设置复选框 问题是复选框在没有特殊原因的情况下被启动为选中状态
  • 如何从firestore数据库中的云功能更新多个文档?

    我是 firebase 云功能的新手 我想更新username一些文件的字段来自posts集合时users收藏改变它username特定文档的字段 我使用以下代码来做到这一点 exports updateProfileUsername fu
  • VC++:KB971090 并选择 Visual C 运行时 DLL 依赖项

    如您所知 Microsoft 最近为 Visual Studio 部署了安全更新 KB971090 http support microsoft com kb 971090 除此之外 这还将 Visual C 运行时 DLL 从版本 8 0
  • Java HttpUrlConnection POST 请求特殊字符奇怪的行为

    我正在尝试使用 HttpURLConnection 实现 POST 请求 这是我的代码 private static void call String body throws IOException HttpURLConnection co
  • 了解结构域突变

    来自锈书 https doc rust lang org book structs html关于如何改变结构体字段 let mut point Point x 0 y 0 point x 5 然后 可变性是绑定的属性 而不是结构本身的属性
  • 为什么 setInterval 不能避免 XSS?

    我正在经历OWASP 跨站脚本防止备忘单 https cheatsheetseries owasp org cheatsheets Cross Site Scripting Prevention Cheat Sheet html 规则 3
  • awk 查找特定日期的最大值

    我有一个包含多行的文件 每行包含以下数据 name 20150801 1 20150802 4 20150803 6 20150804 7 20150805 7 20150806 8 20150807 11532 20150808 1239
  • 如何让java日历从星期一开始工作日?

    我已经编写了代码 它使用 Java 日历并显示时间戳中的 DAY OF WEEK 但默认日历从星期日 1 开始 我希望从星期一开始 例如 星期一应该返回 1 这是我的代码 Calender c Calender getInstance Ti
  • 是否存在从 Eclipse 中的同一代码库维护免费和专业应用程序版本的约定?

    我正在发布一个应用程序的两个版本 免费和付费 两者之间的差异很少 我想使用相同的代码库来发布两者 也许为其他版本定义了不同的常量 有人这样做过吗 有人能指出我正确的方向吗 谢谢 Use an Android 库项目 http develop
  • React 的 setState 方法带有 prevState 参数

    我是 React 新手 只是对 setState 方法有疑问 假设我们有一个组件 class MyApp extends React Component state count 3 Increment gt this setState pr
  • 使用 jQuery 文件上传 - blueimp(基于 Ajax)php / yii 上传超过 1GB 到 2GB 的大文件,它在 Firefox 浏览器中显示错误

    我正在尝试上传一个大文件1GB to 2GB using jQuery File Upload blueimp 基于阿贾克斯 php yii Framework 1 15我已设置这些值来上传更大的文件 memory limit 2048M
  • .NET Linq 左连接

    我在 SQL 中有 2 个表 Table 1 Step Id Step Name Table 2 Profile Id Step Id Completed 即使表 2 中不匹配 我也想返回以下结果 Results Table1 Step I
  • Python + SqlAlchemy:当父子都是新建时添加父子记录

    我问过一个类似的问题 https stackoverflow com q 59097167 2144390已经 但我认为如果我不提供我已经编码的示例 而是简单地陈述我的案例的目标 那么会更清楚 开始 我有一个处于自引用一对多关系的表 即 它
  • 使用字符串输入和输出运行进程

    这里有很多与 fork 和 exec 相关的问题 不过 我还没有找到真正使使用它们的过程变得简单的方法 而让程序员的生活变得简单就是目标 我需要一个 C Linux 友好的函数来执行以下操作 string RunCommand string
  • 如何给函数起别名?

    我正在尝试为 R 中的函数创建别名 例如 要获取 R 中向量的长度 length the vector returns the length of the vector 我想创建一个名为 len 的函数的别名 len the vector
  • 使用 Serde 反序列化具有多种类型字段的 JSON

    我有一些 JSON 文本数据 其字段可以是字符串或字符串数 组 以下是四个可能的示例 keya some string keyb some string keya some string keyb some string some stri
  • 比较 SPARQL 图

    如何使用 SPARQL 比较两个 RDF 图 如果我有图表 a 和 b 我想找到 a 出现在 b 中的所有时间 我可以查询 a 的所有主语 谓词和宾语 然后以编程方式构建一个与 b 中的 a 模式匹配的模式查询 有没有一种方法可以在 SPA
  • xcode 命令行测试,参数在启动时传递

    我在 CI 上实施 xcodebuild 命令测试时遇到了小问题 我有与特定设备语言相关的测试 在 xcode 中我可以将 启动时传递的参数 设置为 AppleLanguages 语言 我可以使用 xcodebuild 传递该参数吗 我的脚
  • 如何用C++封装视频文件的H.264码流

    我正在尝试转换视频文件 mp4 到 Dicom 文件 我通过在 Dicom 中存储单个图像 视频的每帧一个图像 成功地做到了这一点 但结果是文件太大 这对我来说不好 相反 我想将存储在视频文件中的 H 264 比特流封装到 Dicom 文件