comparison OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 1555:d6a93e12b1c1

Creation of DICOM files with encapsulated PDF
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 20 Aug 2015 15:18:13 +0200
parents f967bdf8534e
children ad1e127b4ed5
comparison
equal deleted inserted replaced
1554:89ab71a68fcf 1555:d6a93e12b1c1
426 changeType, resourceType, call); 426 changeType, resourceType, call);
427 } 427 }
428 } 428 }
429 429
430 430
431 static void CreateDicom(RestApiPostCall& call) 431 static bool CreateDicomV1(ParsedDicomFile& dicom,
432 const Json::Value& request)
432 { 433 {
433 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}' 434 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World"}'
434 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}' 435 // curl http://localhost:8042/tools/create-dicom -X POST -d '{"PatientName":"Hello^World","PixelData":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gUGDDcB53FulQAAAElJREFUGNNtj0sSAEEEQ1+U+185s1CtmRkblQ9CZldsKHJDk6DLGLJa6chjh0ooQmpjXMM86zPwydGEj6Ed/UGykkEM8X+p3u8/8LcOJIWLGeMAAAAASUVORK5CYII="}'
435 436
436 Json::Value replacements; 437 assert(request.isObject());
437 if (call.ParseJsonRequest(replacements) && replacements.isObject()) 438 LOG(WARNING) << "Using a deprecated call to /tools/create-dicom";
439
440 Json::Value::Members members = request.getMemberNames();
441 for (size_t i = 0; i < members.size(); i++)
442 {
443 const std::string& name = members[i];
444 if (request[name].type() != Json::stringValue)
445 {
446 LOG(ERROR) << "Only string values are supported when creating DICOM instances";
447 return false;
448 }
449
450 std::string value = request[name].asString();
451
452 DicomTag tag = FromDcmtkBridge::ParseTag(name);
453 if (tag == DICOM_TAG_PIXEL_DATA)
454 {
455 dicom.EmbedImage(value);
456 }
457 else
458 {
459 dicom.Replace(tag, value);
460 }
461 }
462
463 return true;
464 }
465
466
467 static bool CreateDicomV2(ParsedDicomFile& dicom,
468 ServerContext& context,
469 const Json::Value& request)
470 {
471 assert(request.isObject());
472
473 if (!request.isMember("Tags") ||
474 request["Tags"].type() != Json::objectValue)
475 {
476 return false;
477 }
478
479 ResourceType parentType = ResourceType_Instance;
480
481 if (request.isMember("Parent"))
482 {
483 // Locate the parent tags
484 std::string parent = request["Parent"].asString();
485 if (!context.GetIndex().LookupResourceType(parentType, parent))
486 {
487 LOG(ERROR) << "Trying to attach a new DICOM instance to an inexistent resource: " << parent;
488 return false;
489 }
490
491 if (parentType == ResourceType_Instance)
492 {
493 LOG(ERROR) << "Trying to attach a new DICOM instance to an instance (must be a series, study or patient): " << parent;
494 return false;
495 }
496
497 // Select one existing child instance of the parent resource, to
498 // retrieve all its tags
499 Json::Value siblingTags;
500
501 {
502 // Retrieve all the instances of the parent resource
503 std::list<std::string> siblingInstances;
504 context.GetIndex().GetChildInstances(siblingInstances, parent);
505
506 if (siblingInstances.empty())
507 {
508 return false; // Error: No instance (should never happen)
509 }
510
511 context.ReadJson(siblingTags, siblingInstances.front());
512 }
513
514 // Retrieve the tags for all the parent modules
515 typedef std::set<DicomTag> ModuleTags;
516 ModuleTags moduleTags;
517
518 ResourceType type = parentType;
519 for (;;)
520 {
521 DicomTag::AddTagsForModule(moduleTags, GetModule(type));
522
523 if (type == ResourceType_Patient)
524 {
525 break; // We're done
526 }
527
528 // Go up
529 std::string tmp;
530 if (!context.GetIndex().LookupParent(tmp, parent))
531 {
532 return false;
533 }
534
535 parent = tmp;
536 type = GetParentResourceType(type);
537 }
538
539 for (ModuleTags::const_iterator it = moduleTags.begin();
540 it != moduleTags.end(); it++)
541 {
542 std::string t = it->Format();
543 if (siblingTags.isMember(t))
544 {
545 const Json::Value& tag = siblingTags[t];
546 if (tag["Type"] == "Null")
547 {
548 dicom.Replace(*it, "");
549 }
550 else if (tag["Type"] == "String")
551 {
552 dicom.Replace(*it, tag["Value"].asString());
553 }
554 }
555 }
556 }
557
558
559 // Inject time-related information
560 std::string date, time;
561 Toolbox::GetNowDicom(date, time);
562 dicom.Replace(DICOM_TAG_ACQUISITION_DATE, date);
563 dicom.Replace(DICOM_TAG_ACQUISITION_TIME, time);
564 dicom.Replace(DICOM_TAG_CONTENT_DATE, date);
565 dicom.Replace(DICOM_TAG_CONTENT_TIME, time);
566 dicom.Replace(DICOM_TAG_INSTANCE_CREATION_DATE, date);
567 dicom.Replace(DICOM_TAG_INSTANCE_CREATION_TIME, time);
568
569 if (parentType == ResourceType_Patient ||
570 parentType == ResourceType_Study ||
571 parentType == ResourceType_Instance /* no parent */)
572 {
573 dicom.Replace(DICOM_TAG_SERIES_DATE, date);
574 dicom.Replace(DICOM_TAG_SERIES_TIME, time);
575 }
576
577 if (parentType == ResourceType_Patient ||
578 parentType == ResourceType_Instance /* no parent */)
579 {
580 dicom.Replace(DICOM_TAG_STUDY_DATE, date);
581 dicom.Replace(DICOM_TAG_STUDY_TIME, time);
582 }
583
584
585 // Inject the user-specified tags
586 Json::Value::Members members = request["Tags"].getMemberNames();
587 for (size_t i = 0; i < members.size(); i++)
588 {
589 const std::string& name = members[i];
590 if (request["Tags"][name].type() != Json::stringValue)
591 {
592 LOG(ERROR) << "Only string values are supported when creating DICOM instances";
593 return false;
594 }
595
596 std::string value = request["Tags"][name].asString();
597
598 DicomTag tag = FromDcmtkBridge::ParseTag(name);
599 if (dicom.HasTag(tag))
600 {
601 LOG(ERROR) << "Trying to override a value inherited from a parent module";
602 return false;
603 }
604
605 if (tag == DICOM_TAG_PIXEL_DATA)
606 {
607 LOG(ERROR) << "Use \"Content\" to inject an image into a new DICOM instance";
608 return false;
609 }
610 else
611 {
612 dicom.Replace(tag, value);
613 }
614 }
615
616
617 // Inject the content (either an image, or a PDF file)
618 if (request.isMember("Content"))
619 {
620 if (request["Content"].type() != Json::stringValue)
621 {
622 LOG(ERROR) << "The payload of the DICOM instance must be specified according to Data URI scheme";
623 return false;
624 }
625
626 std::string mime, base64;
627 Toolbox::DecodeDataUriScheme(mime, base64, request["Content"].asString());
628 Toolbox::ToLowerCase(mime);
629
630 std::string content;
631 Toolbox::DecodeBase64(content, base64);
632
633 if (mime == "image/png")
634 {
635 dicom.EmbedImage(mime, content);
636 }
637 else if (mime == "application/pdf")
638 {
639 dicom.EmbedPdf(content);
640 }
641 else
642 {
643 LOG(ERROR) << "Unsupported MIME type for the content of a new DICOM file";
644 return false;
645 }
646 }
647
648
649 return true;
650 }
651
652
653 static void CreateDicom(RestApiPostCall& call)
654 {
655 ServerContext& context = OrthancRestApi::GetContext(call);
656
657 Json::Value request;
658 if (call.ParseJsonRequest(request) &&
659 request.isObject())
438 { 660 {
439 ParsedDicomFile dicom; 661 ParsedDicomFile dicom;
440 662
441 Json::Value::Members members = replacements.getMemberNames(); 663 if (request.isMember("Tags") ?
442 for (size_t i = 0; i < members.size(); i++) 664 CreateDicomV2(dicom, context, request) :
443 { 665 CreateDicomV1(dicom, request))
444 const std::string& name = members[i]; 666 {
445 std::string value = replacements[name].asString(); 667 DicomInstanceToStore toStore;
446 668 toStore.SetParsedDicomFile(dicom);
447 DicomTag tag = FromDcmtkBridge::ParseTag(name); 669
448 if (tag == DICOM_TAG_PIXEL_DATA) 670 std::string id;
671 StoreStatus status = OrthancRestApi::GetContext(call).Store(id, toStore);
672
673 if (status == StoreStatus_Failure)
449 { 674 {
450 dicom.EmbedImage(value); 675 LOG(ERROR) << "Error while storing a manually-created instance";
676 return;
451 } 677 }
452 else 678
453 { 679 OrthancRestApi::GetApi(call).AnswerStoredInstance(call, id, status);
454 dicom.Replace(tag, value); 680 }
455 }
456 }
457
458 DicomInstanceToStore toStore;
459 toStore.SetParsedDicomFile(dicom);
460
461 std::string id;
462 StoreStatus status = OrthancRestApi::GetContext(call).Store(id, toStore);
463
464 if (status == StoreStatus_Failure)
465 {
466 LOG(ERROR) << "Error while storing a manually-created instance";
467 return;
468 }
469
470 OrthancRestApi::GetApi(call).AnswerStoredInstance(call, id, status);
471 } 681 }
472 } 682 }
473 683
474 684
475 void OrthancRestApi::RegisterAnonymizeModify() 685 void OrthancRestApi::RegisterAnonymizeModify()