comparison OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 1564:1b7def486e62

creation of DICOM series
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 21 Aug 2015 15:01:21 +0200
parents 2084b7c20478
children 4b23310eb7e8
comparison
equal deleted inserted replaced
1563:9bb416445319 1564:1b7def486e62
37 #include "../../Core/Uuid.h" 37 #include "../../Core/Uuid.h"
38 #include "../FromDcmtkBridge.h" 38 #include "../FromDcmtkBridge.h"
39 #include "../ServerContext.h" 39 #include "../ServerContext.h"
40 #include "../OrthancInitialization.h" 40 #include "../OrthancInitialization.h"
41 41
42 #include <boost/lexical_cast.hpp>
43
42 namespace Orthanc 44 namespace Orthanc
43 { 45 {
44 // Modification of DICOM instances ------------------------------------------ 46 // Modification of DICOM instances ------------------------------------------
45 47
46 enum TagOperation 48 enum TagOperation
427 changeType, resourceType, call); 429 changeType, resourceType, call);
428 } 430 }
429 } 431 }
430 432
431 433
434 static bool StoreCreatedInstance(std::string& id /* out */,
435 ServerContext& context,
436 ParsedDicomFile& dicom)
437 {
438 DicomInstanceToStore toStore;
439 toStore.SetParsedDicomFile(dicom);
440
441 StoreStatus status = context.Store(id, toStore);
442
443 if (status == StoreStatus_Failure)
444 {
445 LOG(ERROR) << "Error while storing a manually-created instance";
446 return false;
447 }
448 else
449 {
450 return true;
451 }
452 }
453
454
432 static bool CreateDicomV1(ParsedDicomFile& dicom, 455 static bool CreateDicomV1(ParsedDicomFile& dicom,
433 const Json::Value& request) 456 const Json::Value& request)
434 { 457 {
435 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}' 458 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}'
436 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}' 459 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}'
463 486
464 return true; 487 return true;
465 } 488 }
466 489
467 490
468 static bool CreateDicomV2(ParsedDicomFile& dicom, 491 static void CreateSeries(RestApiPostCall& call,
469 ServerContext& context, 492 ParsedDicomFile& base /* in */,
493 const Json::Value& content)
494 {
495 assert(content.isArray());
496 assert(content.size() > 0);
497 ServerContext& context = OrthancRestApi::GetContext(call);
498
499 base.Replace(DICOM_TAG_IMAGES_IN_ACQUISITION, boost::lexical_cast<std::string>(content.size()));
500 base.Replace(DICOM_TAG_NUMBER_OF_TEMPORAL_POSITIONS, "1");
501
502 std::string someInstance;
503
504 for (Json::ArrayIndex i = 0; i < content.size(); i++)
505 {
506 if (content[i].type() != Json::stringValue)
507 {
508 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme";
509 return;
510 }
511
512 std::auto_ptr<ParsedDicomFile> dicom(base.Clone());
513 dicom->EmbedContent(content[i].asString());
514 dicom->Replace(DICOM_TAG_INSTANCE_NUMBER, boost::lexical_cast<std::string>(i + 1));
515 dicom->Replace(DICOM_TAG_IMAGE_INDEX, boost::lexical_cast<std::string>(i + 1));
516
517 if (!StoreCreatedInstance(someInstance, context, *dicom))
518 {
519 LOG(ERROR) << "Error while creating the series";
520 return;
521 }
522 }
523
524 std::string series;
525 if (context.GetIndex().LookupParent(series, someInstance))
526 {
527 OrthancRestApi::GetApi(call).AnswerStoredResource(call, series, ResourceType_Series, StoreStatus_Success);
528 }
529 }
530
531
532 static void CreateDicomV2(RestApiPostCall& call,
470 const Json::Value& request) 533 const Json::Value& request)
471 { 534 {
472 assert(request.isObject()); 535 assert(request.isObject());
536 ServerContext& context = OrthancRestApi::GetContext(call);
473 537
474 if (!request.isMember("Tags") || 538 if (!request.isMember("Tags") ||
475 request["Tags"].type() != Json::objectValue) 539 request["Tags"].type() != Json::objectValue)
476 { 540 {
477 return false; 541 return;
478 } 542 }
479 543
480 Encoding encoding; 544 Encoding encoding;
481 545
482 if (request["Tags"].isMember("SpecificCharacterSet")) 546 if (request["Tags"].isMember("SpecificCharacterSet"))
483 { 547 {
484 const char* tmp = request["Tags"]["SpecificCharacterSet"].asCString(); 548 const char* tmp = request["Tags"]["SpecificCharacterSet"].asCString();
485 if (!GetDicomEncoding(encoding, tmp)) 549 if (!GetDicomEncoding(encoding, tmp))
486 { 550 {
487 LOG(ERROR) << "Unknown specific character set: " << tmp; 551 LOG(ERROR) << "Unknown specific character set: " << tmp;
488 return false; 552 return;
489 } 553 }
490 } 554 }
491 else 555 else
492 { 556 {
493 std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1"); 557 std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
494 encoding = StringToEncoding(tmp.c_str()); 558 encoding = StringToEncoding(tmp.c_str());
495 } 559 }
496 560
561 ParsedDicomFile dicom;
497 dicom.SetEncoding(encoding); 562 dicom.SetEncoding(encoding);
498 563
499 ResourceType parentType = ResourceType_Instance; 564 ResourceType parentType = ResourceType_Instance;
500 565
501 if (request.isMember("Parent")) 566 if (request.isMember("Parent"))
503 // Locate the parent tags 568 // Locate the parent tags
504 std::string parent = request["Parent"].asString(); 569 std::string parent = request["Parent"].asString();
505 if (!context.GetIndex().LookupResourceType(parentType, parent)) 570 if (!context.GetIndex().LookupResourceType(parentType, parent))
506 { 571 {
507 LOG(ERROR) << "Trying to attach a new DICOM instance to an inexistent resource: " << parent; 572 LOG(ERROR) << "Trying to attach a new DICOM instance to an inexistent resource: " << parent;
508 return false; 573 return;
509 } 574 }
510 575
511 if (parentType == ResourceType_Instance) 576 if (parentType == ResourceType_Instance)
512 { 577 {
513 LOG(ERROR) << "Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " << parent; 578 LOG(ERROR) << "Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " << parent;
514 return false; 579 return;
515 } 580 }
516 581
517 // Select one existing child instance of the parent resource, to 582 // Select one existing child instance of the parent resource, to
518 // retrieve all its tags 583 // retrieve all its tags
519 Json::Value siblingTags; 584 Json::Value siblingTags;
523 std::list<std::string> siblingInstances; 588 std::list<std::string> siblingInstances;
524 context.GetIndex().GetChildInstances(siblingInstances, parent); 589 context.GetIndex().GetChildInstances(siblingInstances, parent);
525 590
526 if (siblingInstances.empty()) 591 if (siblingInstances.empty())
527 { 592 {
528 return false; // Error: No instance (should never happen) 593 return; // Error: No instance (should never happen)
529 } 594 }
530 595
531 context.ReadJson(siblingTags, siblingInstances.front()); 596 context.ReadJson(siblingTags, siblingInstances.front());
532 } 597 }
533 598
547 612
548 // Go up 613 // Go up
549 std::string tmp; 614 std::string tmp;
550 if (!context.GetIndex().LookupParent(tmp, parent)) 615 if (!context.GetIndex().LookupParent(tmp, parent))
551 { 616 {
552 return false; 617 return;
553 } 618 }
554 619
555 parent = tmp; 620 parent = tmp;
556 type = GetParentResourceType(type); 621 type = GetParentResourceType(type);
557 } 622 }
609 { 674 {
610 const std::string& name = members[i]; 675 const std::string& name = members[i];
611 if (request["Tags"][name].type() != Json::stringValue) 676 if (request["Tags"][name].type() != Json::stringValue)
612 { 677 {
613 LOG(ERROR) << "Only string values are supported when creating DICOM instances"; 678 LOG(ERROR) << "Only string values are supported when creating DICOM instances";
614 return false; 679 return;
615 } 680 }
616 681
617 std::string value = request["Tags"][name].asString(); 682 std::string value = request["Tags"][name].asString();
618 683
619 DicomTag tag = FromDcmtkBridge::ParseTag(name); 684 DicomTag tag = FromDcmtkBridge::ParseTag(name);
620 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET) 685 if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
621 { 686 {
622 if (dicom.HasTag(tag)) 687 if (dicom.HasTag(tag))
623 { 688 {
624 LOG(ERROR) << "Trying to override a value inherited from a parent module"; 689 LOG(ERROR) << "Trying to override a value inherited from a parent module";
625 return false; 690 return;
626 } 691 }
627 692
628 if (tag == DICOM_TAG_PIXEL_DATA) 693 if (tag == DICOM_TAG_PIXEL_DATA)
629 { 694 {
630 LOG(ERROR) << "Use \"Content\" to inject an image into a new DICOM instance"; 695 LOG(ERROR) << "Use \"Content\" to inject an image into a new DICOM instance";
631 return false; 696 return;
632 } 697 }
633 else 698 else
634 { 699 {
635 dicom.Replace(tag, Toolbox::ConvertFromUtf8(value, encoding)); 700 dicom.Replace(tag, Toolbox::ConvertFromUtf8(value, encoding));
636 } 701 }
637 } 702 }
638 } 703 }
639
640 704
641 // Inject the content (either an image, or a PDF file) 705 // Inject the content (either an image, or a PDF file)
642 if (request.isMember("Content")) 706 if (request.isMember("Content"))
643 { 707 {
644 if (request["Content"].type() != Json::stringValue) 708 const Json::Value& content = request["Content"];
709
710 if (content.type() == Json::stringValue)
711 {
712 dicom.EmbedContent(request["Content"].asString());
713
714 }
715 else if (content.type() == Json::arrayValue)
716 {
717 if (content.size() > 0)
718 {
719 // Let's create a series
720 CreateSeries(call, dicom, content);
721 return;
722 }
723 }
724 else
645 { 725 {
646 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme"; 726 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme";
647 return false; 727 return;
648 } 728 }
649 729 }
650 dicom.EmbedContent(request["Content"].asString()); 730
651 } 731 std::string id;
652 732 if (StoreCreatedInstance(id, context, dicom))
653 733 {
654 return true; 734 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
735 }
736
737 return;
655 } 738 }
656 739
657 740
658 static void CreateDicom(RestApiPostCall& call) 741 static void CreateDicom(RestApiPostCall& call)
659 { 742 {
660 ServerContext& context = OrthancRestApi::GetContext(call);
661
662 Json::Value request; 743 Json::Value request;
663 if (call.ParseJsonRequest(request) && 744 if (call.ParseJsonRequest(request) &&
664 request.isObject()) 745 request.isObject())
665 { 746 {
666 ParsedDicomFile dicom; 747 if (request.isMember("Tags"))
667 748 {
668 if (request.isMember("Tags") ? 749 CreateDicomV2(call, request);
669 CreateDicomV2(dicom, context, request) : 750 }
670 CreateDicomV1(dicom, request)) 751 else
671 { 752 {
672 DicomInstanceToStore toStore; 753 // Compatibility with Orthanc <= 0.9.3
673 toStore.SetParsedDicomFile(dicom); 754 ServerContext& context = OrthancRestApi::GetContext(call);
674 755 ParsedDicomFile dicom;
675 std::string id; 756 if (CreateDicomV1(dicom, request))
676 StoreStatus status = OrthancRestApi::GetContext(call).Store(id, toStore); 757 {
677 758 std::string id;
678 if (status == StoreStatus_Failure) 759 if (StoreCreatedInstance(id, context, dicom))
679 { 760 {
680 LOG(ERROR) << "Error while storing a manually-created instance"; 761 OrthancRestApi::GetApi(call).AnswerStoredResource(call, id, ResourceType_Instance, StoreStatus_Success);
681 return; 762 }
682 } 763 }
683
684 OrthancRestApi::GetApi(call).AnswerStoredInstance(call, id, status);
685 } 764 }
686 } 765 }
687 } 766 }
688 767
689 768