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