Mercurial > hg > orthanc
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 |