读取heic格式图像主要通过第三方库实现,首先创建读取的入口,在前面几期中讲过增加“打开”菜单按钮实现功能。
增加“打开图片”菜单:
QMenu *pMenu = menuBar()->addMenu(QStringLiteral("文件(&F)"));
QAction *pOpenFile = new QAction(QStringLiteral("打开图片(&O)"));
connect(pOpenFile, &QAction::triggered, this, &heicExample::OpenFile);
QAction *pExit = new QAction(QStringLiteral("退出(&Q)"));
pMenu->addAction(pOpenFile);
pMenu->addAction(pExit);
实现槽函数OpenFile:
void heicExample::OpenFile()
{
QString fileName = QFileDialog::getOpenFileName(
this,
tr("open image file"), "./",
tr("Image files(*.bmp *.jpg *.pbm *.pgm *.png *.ppm *.xbm *.xpm *.heic);;All files (*.*)"));
if (pixItem && fileName != "")
{
m_graphicsScene->removeItem(pixItem); //将上一个图元从场景中移除,重新添加新的图元
delete pixItem;
pixItem = NULL;
for (int i = 0; i < m_loldrgb.length(); i++)
{
delete m_loldrgb[i];
m_loldrgb[i] = NULL;
}
m_loldrgb.clear();
}
if(fileName.contains(".heic"))
{
ReadHeif(fileName);
}
}
读取heic格式文件:
void heicExample::ReadHeif(QString filename)
{
filename = QDir::fromNativeSeparators(filename);
Array<ImageId> itemIds;
const char* outputFileName = "./abc";
// Make an instance of Heif Reader
auto *reader = Reader::Create();
if (reader->initialize(filename.toLatin1().data()) != ErrorCode::OK)
return;
// Verify that the file is HEIF format.
FileInformation fileInfo;
if (reader->getFileInformation(fileInfo) != ErrorCode::OK) {
cout << "Unable to get MetaBox! Wrong heif format." << endl;
return;
}
if (!(fileInfo.features & FileFeatureEnum::HasSingleImage ||
fileInfo.features & FileFeatureEnum::HasImageCollection)) {
cout << "The file don't have images in the Metabox. " << endl;
return;
}
uint64_t memoryBufferSize = 1024 * 1024;
ofstream outfile(outputFileName, ofstream::binary);
auto *memoryBuffer = new uint8_t[memoryBufferSize];
const auto metaBoxFeatures = fileInfo.rootMetaBoxInformation.features;
if (metaBoxFeatures & MetaBoxFeatureEnum::HasThumbnails)
{
//reader->getMasterImages(itemIds);
//const ImageId masterId = itemIds[0];
cout << "The file have Thumbnail." << endl;
// Thumbnail references ('thmb') are from the thumbnail image to the master image
//reader->getReferencedToItemListByType(masterId, "thmb", itemIds);
//const auto thumbnailId = itemIds[0];
//if (reader->getItemDataWithDecoderParameters(thumbnailId.get(), memoryBuffer, memoryBufferSize) == ErrorCode::OK)
//{
// ...decode data and display the image, show master image later
// EncodePlay(memoryBuffer, memoryBufferSize);
// return;
//}
}
// when the file is iPhone heic.
Array<ImageId> gridIds;
Grid gridData;
if (reader->getItemListByType("grid", gridIds) == ErrorCode::OK &&
(fileInfo.features & FileFeatureEnum::HasImageCollection) &&
reader->getItem(gridIds[0].get(), gridData) == ErrorCode::OK)
{
//return iphoneHeic(reader, gridData, outputFileName);
qDebug("iphone heic");
}
// The image have collections
if (fileInfo.features & FileFeatureEnum::HasImageCollection)
{
Array<ImageId> itemIds;
reader->getMasterImages(itemIds);
// all the image items
for (unsigned int i = 0; i < itemIds.size; i++) {
const ImageId masterId = itemIds[i];
if (reader->getItemDataWithDecoderParameters(
masterId.get(), memoryBuffer, memoryBufferSize) == ErrorCode::OK) {
DecoderConfiguration decodeConf; // struct containing
reader->getDecoderParameterSets(masterId.get(), decodeConf);
auto decoSpeInfo = decodeConf.decoderSpecificInfo;
for (unsigned int j = 0; j < decoSpeInfo.size; ++j) {
auto entry = decoSpeInfo[j];
outfile.write((const char *)entry.decSpecInfoData.begin(),
entry.decSpecInfoData.size);
}
outfile.write(reinterpret_cast<const char*>(memoryBuffer), memoryBufferSize);
if (i == 0)
{
EncodePlay(memoryBuffer, memoryBufferSize);
}
}
else { cout << "getItemDataWithDecoderParameters error" << endl; }
};
delete[] memoryBuffer;
Reader::Destroy(reader);
return;
}// The image only have 1 master image.
else if (fileInfo.features & FileFeatureEnum::HasSingleImage)
{
Array<ImageId> itemIds;
reader->getMasterImages(itemIds);
// Find the item ID of the first master image
const ImageId masterId = itemIds[0];
if (reader->getItemDataWithDecoderParameters(masterId.get(), memoryBuffer, memoryBufferSize) == ErrorCode::OK)
{
outfile.write(reinterpret_cast<const char*>(memoryBuffer), memoryBufferSize);
cout << "Get the master image" << endl;
// ffmpeg
// Get HEVC decoder configuration
EncodePlay(memoryBuffer, memoryBufferSize);
}
}
cout << "There no image in the file MetaBox! ";
Reader::Destroy(reader);
return;
}
通过EncodePlay函数解析heic格式图像:
void heicExample::EncodePlay(uint8_t * pData, int nDataLength)
{
AVPacket packet;
av_init_packet(&packet);
packet.data = (uint8_t*)pData;
packet.size = nDataLength;
packet.pts = (90000 / 25) * m_nFrameCounter++;
int ret = -1;
ret = avcodec_send_packet(m_pCodecContext, &packet);
// Again EAGAIN is not expected
if (ret < 0)
{
return;
}
while (!ret)
{
ret = avcodec_receive_frame(m_pCodecContext, m_pSrcFrame);
if (!ret)
{
//成功解码一帧
int w = m_pCodecContext->width;
int h = m_pCodecContext->height;
if (m_pRGBSwsContext == NULL)
{
m_pRGBSwsContext = sws_getContext(w, h, m_pCodecContext->pix_fmt, w, h, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
av_image_alloc(m_pFrameRGB->data, m_pFrameRGB->linesize, w, h, AV_PIX_FMT_RGB32, 1);
}
//转换图像格式,将解压出来的YUV420P的图像转换为RGB的图像
sws_scale(m_pRGBSwsContext, (uint8_t const * const *)m_pSrcFrame->data, m_pSrcFrame->linesize, 0, h, m_pFrameRGB->data, m_pFrameRGB->linesize);
//把这个RGB数据 用QImage加载
QImage tmpImg((uchar *)m_pFrameRGB->data[0], m_pCodecContext->width, m_pCodecContext->height, QImage::Format_RGB32);
image = tmpImg.copy();
//把图像复制一份 传递给界面显示
//emit signal_sendQImage(image);
//ui.label->resize(w, h);
//QImage img = image.scaled(ui.label->size(), Qt::KeepAspectRatio);
//ui.label->setPixmap(QPixmap::fromImage(img));
pixItem = new PixItem(QPixmap::fromImage(image));
//将该图元对象添加到场景中,并设置此图元在场景中的位置为中心(0,0)
m_graphicsScene->addItem(pixItem);
pixItem->setPos(0, 0);
//保存原来的颜色
for (int x = 0; x < image.width(); x++) {
for (int y = 0; y < image.height(); y++) {
QColor *oldColor = new QColor(image.pixel(x, y));
m_loldrgb.append(oldColor);
}
}
av_frame_unref(m_pSrcFrame);
}
}
}
通过ffmpeg解码heic格式,然后转换成RGB32格式,最后通过画布进行展示。