读取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格式,最后通过画布进行展示。