comparison ViewerPlugin/Plugin.cpp @ 257:9af4ba0d92fe iiif

support of full rendering in IIIF
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sun, 09 Jul 2023 15:24:37 +0200
parents 7deea131c3c0
children 07be799fa383
comparison
equal deleted inserted replaced
256:7deea131c3c0 257:9af4ba0d92fe
487 487
488 LOG(INFO) << "IIIF: Accessing tile of series " << seriesId << ": " 488 LOG(INFO) << "IIIF: Accessing tile of series " << seriesId << ": "
489 << "region=" << region << "; size=" << size << "; rotation=" 489 << "region=" << region << "; size=" << size << "; rotation="
490 << rotation << "; quality=" << quality << "; format=" << format; 490 << rotation << "; quality=" << quality << "; format=" << format;
491 491
492 if (rotation != "0")
493 {
494 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Unsupported rotation: " + rotation);
495 }
496
497 if (quality != "default")
498 {
499 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Unsupported quality: " + quality);
500 }
501
502 if (format != "jpg")
503 {
504 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Unsupported format: " + format);
505 }
506
492 if (region == "full") 507 if (region == "full")
493 { 508 {
494 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Full region is not supported for whole-slide images");
495 }
496
497 if (rotation != "0")
498 {
499 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Unsupported rotation: " + rotation);
500 }
501
502 if (quality != "default")
503 {
504 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Unsupported quality: " + quality);
505 }
506
507 if (format != "jpg")
508 {
509 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Unsupported format: " + format);
510 }
511
512 int regionX, regionY, regionWidth, regionHeight;
513
514 bool ok = false;
515 boost::regex regionPattern("([0-9]+),([0-9]+),([0-9]+),([0-9]+)");
516 boost::cmatch regionWhat;
517 if (regex_match(region.c_str(), regionWhat, regionPattern))
518 {
519 try
520 {
521 regionX = boost::lexical_cast<int>(regionWhat[1]);
522 regionY = boost::lexical_cast<int>(regionWhat[2]);
523 regionWidth = boost::lexical_cast<int>(regionWhat[3]);
524 regionHeight = boost::lexical_cast<int>(regionWhat[4]);
525 ok = (regionX >= 0 &&
526 regionY >= 0 &&
527 regionWidth > 0 &&
528 regionHeight > 0);
529 }
530 catch (boost::bad_lexical_cast&)
531 {
532 }
533 }
534
535 if (!ok)
536 {
537 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Not a (x,y,width,height) region: " + region);
538 }
539
540 int cropWidth;
541 boost::regex sizePattern("([0-9]+),");
542 boost::cmatch sizeWhat;
543 if (regex_match(size.c_str(), sizeWhat, sizePattern))
544 {
545 try
546 {
547 cropWidth = boost::lexical_cast<int>(sizeWhat[1]);
548 ok = (cropWidth > 0);
549 }
550 catch (boost::bad_lexical_cast&)
551 {
552 }
553 }
554
555 if (!ok)
556 {
557 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Not a (width,) size: " + size);
558 }
559
560 std::unique_ptr<RawTile> rawTile;
561 std::unique_ptr<Orthanc::ImageAccessor> toCrop;
562
563 {
564 OrthancWSI::DicomPyramidCache::Locker locker(*cache_, seriesId); 509 OrthancWSI::DicomPyramidCache::Locker locker(*cache_, seriesId);
565 510
566 OrthancWSI::ITiledPyramid& pyramid = locker.GetPyramid(); 511 OrthancWSI::ITiledPyramid& pyramid = locker.GetPyramid();
567 512 const unsigned int level = pyramid.GetLevelCount() - 1;
568 unsigned int level; 513
569 for (level = 0; level < pyramid.GetLevelCount(); level++) 514 Orthanc::Image full(Orthanc::PixelFormat_RGB24, pyramid.GetLevelWidth(level), pyramid.GetLevelHeight(level), false);
570 { 515 Orthanc::ImageProcessing::Set(full, 255, 255, 255, 0);
571 const unsigned int physicalTileWidth = GetPhysicalTileWidth(pyramid, level); 516
572 const unsigned int physicalTileHeight = GetPhysicalTileHeight(pyramid, level); 517 const unsigned int nx = OrthancWSI::CeilingDivision(pyramid.GetLevelWidth(level), pyramid.GetTileWidth(level));
573 518 const unsigned int ny = OrthancWSI::CeilingDivision(pyramid.GetLevelHeight(level), pyramid.GetTileHeight(level));
574 if (regionX % physicalTileWidth == 0 && 519 for (unsigned int ty = 0; ty < ny; ty++)
575 regionY % physicalTileHeight == 0 && 520 {
576 regionWidth <= physicalTileWidth && 521 const unsigned int y = ty * pyramid.GetTileHeight(level);
577 regionHeight <= physicalTileHeight) 522 const unsigned int height = std::min(pyramid.GetTileHeight(level), full.GetHeight() - y);
578 { 523
579 break; 524 for (unsigned int tx = 0; tx < nx; tx++)
580 } 525 {
581 } 526 const unsigned int x = tx * pyramid.GetTileWidth(level);
582 527 std::unique_ptr<Orthanc::ImageAccessor> tile(pyramid.DecodeTile(level, tx, ty));
583 if (cropWidth > pyramid.GetTileWidth(level)) 528
584 { 529 const unsigned int width = std::min(pyramid.GetTileWidth(level), full.GetWidth() - x);
585 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "IIIF - Request for a cropping that is too large for the tile size"); 530
586 } 531 Orthanc::ImageAccessor source, target;
587 532 tile->GetRegion(source, 0, 0, width, height);
588 if (level == pyramid.GetLevelCount()) 533 full.GetRegion(target, x, y, width, height);
589 { 534
590 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "IIIF - Cannot locate the level of interest"); 535 Orthanc::ImageProcessing::Copy(target, source);
591 } 536 }
592 else 537 }
593 {
594 rawTile.reset(new RawTile(locker.GetPyramid(), level,
595 regionX / GetPhysicalTileWidth(pyramid, level),
596 regionY / GetPhysicalTileHeight(pyramid, level)));
597
598 if (cropWidth < pyramid.GetTileWidth(level))
599 {
600 toCrop.reset(rawTile->Decode());
601 rawTile.reset(NULL);
602 }
603 }
604 }
605
606 if (rawTile.get() != NULL)
607 {
608 assert(toCrop.get() == NULL);
609
610 // Level 0 Compliance of IIIF expects JPEG files
611 rawTile->Answer(output, Orthanc::MimeType_Jpeg);
612 }
613 else if (toCrop.get() != NULL)
614 {
615 assert(rawTile.get() == NULL);
616 assert(cropWidth < toCrop->GetWidth());
617
618 Orthanc::ImageAccessor cropped;
619 toCrop->GetRegion(cropped, 0, 0, cropWidth, toCrop->GetHeight());
620 538
621 std::string encoded; 539 std::string encoded;
622 RawTile::Encode(encoded, cropped, Orthanc::MimeType_Jpeg); 540 RawTile::Encode(encoded, full, Orthanc::MimeType_Jpeg);
623 541
624 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, encoded.c_str(), 542 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, encoded.c_str(),
625 encoded.size(), Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg)); 543 encoded.size(), Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg));
626 } 544 }
627 else 545 else
628 { 546 {
629 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 547 int regionX, regionY, regionWidth, regionHeight;
548
549 bool ok = false;
550 boost::regex regionPattern("([0-9]+),([0-9]+),([0-9]+),([0-9]+)");
551 boost::cmatch regionWhat;
552 if (regex_match(region.c_str(), regionWhat, regionPattern))
553 {
554 try
555 {
556 regionX = boost::lexical_cast<int>(regionWhat[1]);
557 regionY = boost::lexical_cast<int>(regionWhat[2]);
558 regionWidth = boost::lexical_cast<int>(regionWhat[3]);
559 regionHeight = boost::lexical_cast<int>(regionWhat[4]);
560 ok = (regionX >= 0 &&
561 regionY >= 0 &&
562 regionWidth > 0 &&
563 regionHeight > 0);
564 }
565 catch (boost::bad_lexical_cast&)
566 {
567 }
568 }
569
570 if (!ok)
571 {
572 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Not a (x,y,width,height) region: " + region);
573 }
574
575 int cropWidth;
576 boost::regex sizePattern("([0-9]+),");
577 boost::cmatch sizeWhat;
578 if (regex_match(size.c_str(), sizeWhat, sizePattern))
579 {
580 try
581 {
582 cropWidth = boost::lexical_cast<int>(sizeWhat[1]);
583 ok = (cropWidth > 0);
584 }
585 catch (boost::bad_lexical_cast&)
586 {
587 }
588 }
589
590 if (!ok)
591 {
592 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, "IIIF - Not a (width,) size: " + size);
593 }
594
595 std::unique_ptr<RawTile> rawTile;
596 std::unique_ptr<Orthanc::ImageAccessor> toCrop;
597
598 {
599 OrthancWSI::DicomPyramidCache::Locker locker(*cache_, seriesId);
600
601 OrthancWSI::ITiledPyramid& pyramid = locker.GetPyramid();
602
603 unsigned int level;
604 for (level = 0; level < pyramid.GetLevelCount(); level++)
605 {
606 const unsigned int physicalTileWidth = GetPhysicalTileWidth(pyramid, level);
607 const unsigned int physicalTileHeight = GetPhysicalTileHeight(pyramid, level);
608
609 if (regionX % physicalTileWidth == 0 &&
610 regionY % physicalTileHeight == 0 &&
611 regionWidth <= physicalTileWidth &&
612 regionHeight <= physicalTileHeight)
613 {
614 break;
615 }
616 }
617
618 if (cropWidth > pyramid.GetTileWidth(level))
619 {
620 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "IIIF - Request for a cropping that is too large for the tile size");
621 }
622
623 if (level == pyramid.GetLevelCount())
624 {
625 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "IIIF - Cannot locate the level of interest");
626 }
627 else
628 {
629 rawTile.reset(new RawTile(locker.GetPyramid(), level,
630 regionX / GetPhysicalTileWidth(pyramid, level),
631 regionY / GetPhysicalTileHeight(pyramid, level)));
632
633 if (cropWidth < pyramid.GetTileWidth(level))
634 {
635 toCrop.reset(rawTile->Decode());
636 rawTile.reset(NULL);
637 }
638 }
639 }
640
641 if (rawTile.get() != NULL)
642 {
643 assert(toCrop.get() == NULL);
644
645 // Level 0 Compliance of IIIF expects JPEG files
646 rawTile->Answer(output, Orthanc::MimeType_Jpeg);
647 }
648 else if (toCrop.get() != NULL)
649 {
650 assert(rawTile.get() == NULL);
651 assert(cropWidth < toCrop->GetWidth());
652
653 Orthanc::ImageAccessor cropped;
654 toCrop->GetRegion(cropped, 0, 0, cropWidth, toCrop->GetHeight());
655
656 std::string encoded;
657 RawTile::Encode(encoded, cropped, Orthanc::MimeType_Jpeg);
658
659 OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, encoded.c_str(),
660 encoded.size(), Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg));
661 }
662 else
663 {
664 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
665 }
630 } 666 }
631 } 667 }
632 668
633 669
634 extern "C" 670 extern "C"