Mercurial > hg > orthanc
comparison OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.cpp @ 4734:b51c08bd5c38
added ITagVisitor::Action_Remove
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 05 Jul 2021 16:12:10 +0200 |
parents | 569d9ef165b1 |
children | 979ae3ea3381 |
comparison
equal
deleted
inserted
replaced
4733:1db3b79d97bd | 4734:b51c08bd5c38 |
---|---|
2338 } | 2338 } |
2339 | 2339 |
2340 | 2340 |
2341 | 2341 |
2342 // Forward declaration | 2342 // Forward declaration |
2343 static void ApplyVisitorToElement(DcmElement& element, | 2343 static bool ApplyVisitorToElement(DcmElement& element, |
2344 ITagVisitor& visitor, | 2344 ITagVisitor& visitor, |
2345 const std::vector<DicomTag>& parentTags, | 2345 const std::vector<DicomTag>& parentTags, |
2346 const std::vector<size_t>& parentIndexes, | 2346 const std::vector<size_t>& parentIndexes, |
2347 Encoding encoding, | 2347 Encoding encoding, |
2348 bool hasCodeExtensions); | 2348 bool hasCodeExtensions); |
2354 Encoding encoding, | 2354 Encoding encoding, |
2355 bool hasCodeExtensions) | 2355 bool hasCodeExtensions) |
2356 { | 2356 { |
2357 assert(parentTags.size() == parentIndexes.size()); | 2357 assert(parentTags.size() == parentIndexes.size()); |
2358 | 2358 |
2359 std::set<DcmTagKey> toRemove; | |
2360 | |
2359 for (unsigned long i = 0; i < dataset.card(); i++) | 2361 for (unsigned long i = 0; i < dataset.card(); i++) |
2360 { | 2362 { |
2361 DcmElement* element = dataset.getElement(i); | 2363 DcmElement* element = dataset.getElement(i); |
2362 if (element == NULL) | 2364 if (element == NULL) |
2363 { | 2365 { |
2364 throw OrthancException(ErrorCode_InternalError); | 2366 throw OrthancException(ErrorCode_InternalError); |
2365 } | 2367 } |
2366 else | 2368 else |
2367 { | 2369 { |
2368 ApplyVisitorToElement(*element, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions); | 2370 if (!ApplyVisitorToElement(*element, visitor, parentTags, parentIndexes, encoding, hasCodeExtensions)) |
2371 { | |
2372 toRemove.insert(element->getTag()); | |
2373 } | |
2369 } | 2374 } |
2370 } | 2375 } |
2371 } | 2376 |
2372 | 2377 // Remove all the tags that were planned for removal (cf. ITagVisitor::Action_Remove) |
2373 | 2378 for (std::set<DcmTagKey>::const_iterator |
2374 static void ApplyVisitorToLeaf(DcmElement& element, | 2379 it = toRemove.begin(); it != toRemove.end(); ++it) |
2380 { | |
2381 std::unique_ptr<DcmElement> tmp(dataset.remove(*it)); | |
2382 } | |
2383 } | |
2384 | |
2385 | |
2386 // Returns "true" iff the element must be kept. If "false" is | |
2387 // returned, the element will be removed. | |
2388 static bool ApplyVisitorToLeaf(DcmElement& element, | |
2375 ITagVisitor& visitor, | 2389 ITagVisitor& visitor, |
2376 const std::vector<DicomTag>& parentTags, | 2390 const std::vector<DicomTag>& parentTags, |
2377 const std::vector<size_t>& parentIndexes, | 2391 const std::vector<size_t>& parentIndexes, |
2378 const DicomTag& tag, | 2392 const DicomTag& tag, |
2379 Encoding encoding, | 2393 Encoding encoding, |
2413 evr == EVR_UN) // unknown value representation | 2427 evr == EVR_UN) // unknown value representation |
2414 { | 2428 { |
2415 Uint16* data16 = NULL; | 2429 Uint16* data16 = NULL; |
2416 Uint8* data = NULL; | 2430 Uint8* data = NULL; |
2417 | 2431 |
2432 ITagVisitor::Action action; | |
2433 | |
2418 if ((element.getTag() == DCM_PixelData || // (*) New in Orthanc 1.9.1 | 2434 if ((element.getTag() == DCM_PixelData || // (*) New in Orthanc 1.9.1 |
2419 evr == EVR_OW) && | 2435 evr == EVR_OW) && |
2420 element.getUint16Array(data16) == EC_Normal) | 2436 element.getUint16Array(data16) == EC_Normal) |
2421 { | 2437 { |
2422 visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data16, element.getLength()); | 2438 action = visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data16, element.getLength()); |
2423 } | 2439 } |
2424 else if (evr != EVR_OW && | 2440 else if (evr != EVR_OW && |
2425 element.getUint8Array(data) == EC_Normal) | 2441 element.getUint8Array(data) == EC_Normal) |
2426 { | 2442 { |
2427 /** | 2443 /** |
2430 * to pixel data, during the call to "swapIfNecessary()" in | 2446 * to pixel data, during the call to "swapIfNecessary()" in |
2431 * "DcmPolymorphOBOW::getUint8Array()" (this method is not | 2447 * "DcmPolymorphOBOW::getUint8Array()" (this method is not |
2432 * reimplemented in derived class "DcmPixelData"). However, | 2448 * reimplemented in derived class "DcmPixelData"). However, |
2433 * "getUint16Array()" works correctly, hence (*). | 2449 * "getUint16Array()" works correctly, hence (*). |
2434 **/ | 2450 **/ |
2435 visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data, element.getLength()); | 2451 action = visitor.VisitBinary(parentTags, parentIndexes, tag, vr, data, element.getLength()); |
2436 } | 2452 } |
2437 else | 2453 else |
2438 { | 2454 { |
2439 visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); | 2455 action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); |
2440 } | 2456 } |
2441 | 2457 |
2442 return; // We're done | 2458 switch (action) |
2459 { | |
2460 case ITagVisitor::Action_None: | |
2461 return true; // We're done | |
2462 | |
2463 case ITagVisitor::Action_Remove: | |
2464 return false; | |
2465 | |
2466 case ITagVisitor::Action_Replace: | |
2467 throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace binary data"); | |
2468 | |
2469 default: | |
2470 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
2471 } | |
2443 } | 2472 } |
2444 | 2473 |
2445 | 2474 |
2446 /** | 2475 /** |
2447 * Deal with plain strings (and convert them to UTF-8) | 2476 * Deal with plain strings (and convert them to UTF-8) |
2471 (newValue, parentTags, parentIndexes, tag, vr, utf8); | 2500 (newValue, parentTags, parentIndexes, tag, vr, utf8); |
2472 | 2501 |
2473 switch (action) | 2502 switch (action) |
2474 { | 2503 { |
2475 case ITagVisitor::Action_None: | 2504 case ITagVisitor::Action_None: |
2476 break; | 2505 return true; |
2506 | |
2507 case ITagVisitor::Action_Remove: | |
2508 return false; | |
2477 | 2509 |
2478 case ITagVisitor::Action_Replace: | 2510 case ITagVisitor::Action_Replace: |
2479 { | 2511 { |
2480 std::string s = Toolbox::ConvertFromUtf8(newValue, encoding); | 2512 std::string s = Toolbox::ConvertFromUtf8(newValue, encoding); |
2481 if (element.putString(s.c_str()) != EC_Normal) | 2513 if (element.putString(s.c_str()) != EC_Normal) |
2482 { | 2514 { |
2483 throw OrthancException(ErrorCode_InternalError, | 2515 throw OrthancException(ErrorCode_InternalError, |
2484 "Cannot replace value of tag: " + tag.Format()); | 2516 "Iterator cannot replace value of tag: " + tag.Format()); |
2485 } | 2517 } |
2486 | 2518 |
2487 break; | 2519 return true; |
2488 } | 2520 } |
2489 | 2521 |
2490 default: | 2522 default: |
2491 throw OrthancException(ErrorCode_InternalError); | 2523 throw OrthancException(ErrorCode_InternalError); |
2492 } | 2524 } |
2493 | 2525 } |
2494 return; // We're done | 2526 |
2495 } | 2527 |
2496 | 2528 ITagVisitor::Action action; |
2497 | 2529 |
2498 try | 2530 try |
2499 { | 2531 { |
2500 // http://support.dcmtk.org/docs/dcvr_8h-source.html | 2532 // http://support.dcmtk.org/docs/dcvr_8h-source.html |
2501 switch (evr) | 2533 switch (evr) |
2502 { | 2534 { |
2520 case EVR_UT: // unlimited text | 2552 case EVR_UT: // unlimited text |
2521 case EVR_PN: // person name | 2553 case EVR_PN: // person name |
2522 case EVR_UI: // unique identifier | 2554 case EVR_UI: // unique identifier |
2523 { | 2555 { |
2524 Uint8* data = NULL; | 2556 Uint8* data = NULL; |
2525 | 2557 |
2526 if (element.getUint8Array(data) == EC_Normal) | 2558 if (element.getUint8Array(data) == EC_Normal) |
2527 { | 2559 { |
2528 const Uint32 length = element.getLength(); | 2560 const Uint32 length = element.getLength(); |
2529 Uint32 l = 0; | 2561 Uint32 l = 0; |
2530 while (l < length && | 2562 while (l < length && |
2534 } | 2566 } |
2535 | 2567 |
2536 if (l == length) | 2568 if (l == length) |
2537 { | 2569 { |
2538 // Not a null-terminated plain string | 2570 // Not a null-terminated plain string |
2539 visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); | 2571 action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); |
2540 } | 2572 } |
2541 else | 2573 else |
2542 { | 2574 { |
2543 std::string ignored; | 2575 std::string ignored; |
2544 std::string s(reinterpret_cast<const char*>(data), l); | 2576 std::string s(reinterpret_cast<const char*>(data), l); |
2545 ITagVisitor::Action action = visitor.VisitString | 2577 action = visitor.VisitString(ignored, parentTags, parentIndexes, tag, vr, |
2546 (ignored, parentTags, parentIndexes, tag, vr, | 2578 Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions)); |
2547 Toolbox::ConvertToUtf8(s, encoding, hasCodeExtensions)); | |
2548 | |
2549 if (action != ITagVisitor::Action_None) | |
2550 { | |
2551 LOG(WARNING) << "Cannot replace this string tag: " | |
2552 << FromDcmtkBridge::GetTagName(element) | |
2553 << " (" << tag.Format() << ")"; | |
2554 } | |
2555 } | 2579 } |
2556 } | 2580 } |
2557 else | 2581 else |
2558 { | 2582 { |
2559 visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); | 2583 action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); |
2560 } | 2584 } |
2561 | 2585 |
2562 return; | 2586 if (action == ITagVisitor::Action_Replace) |
2587 { | |
2588 LOG(WARNING) << "Iterator cannot replace this string tag: " | |
2589 << FromDcmtkBridge::GetTagName(element) | |
2590 << " (" << tag.Format() << ")"; | |
2591 return true; | |
2592 } | |
2593 | |
2594 break; | |
2563 } | 2595 } |
2564 | 2596 |
2565 /** | 2597 /** |
2566 * Numeric types | 2598 * Numeric types |
2567 **/ | 2599 **/ |
2580 { | 2612 { |
2581 values.push_back(f); | 2613 values.push_back(f); |
2582 } | 2614 } |
2583 } | 2615 } |
2584 | 2616 |
2585 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); | 2617 action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); |
2586 break; | 2618 break; |
2587 } | 2619 } |
2588 | 2620 |
2589 case EVR_SS: // signed short | 2621 case EVR_SS: // signed short |
2590 { | 2622 { |
2600 { | 2632 { |
2601 values.push_back(f); | 2633 values.push_back(f); |
2602 } | 2634 } |
2603 } | 2635 } |
2604 | 2636 |
2605 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); | 2637 action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); |
2606 break; | 2638 break; |
2607 } | 2639 } |
2608 | 2640 |
2609 case EVR_UL: // unsigned long | 2641 case EVR_UL: // unsigned long |
2610 #if DCMTK_VERSION_NUMBER >= 362 | 2642 #if DCMTK_VERSION_NUMBER >= 362 |
2623 { | 2655 { |
2624 values.push_back(f); | 2656 values.push_back(f); |
2625 } | 2657 } |
2626 } | 2658 } |
2627 | 2659 |
2628 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); | 2660 action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); |
2629 break; | 2661 break; |
2630 } | 2662 } |
2631 | 2663 |
2632 case EVR_US: // unsigned short | 2664 case EVR_US: // unsigned short |
2633 { | 2665 { |
2643 { | 2675 { |
2644 values.push_back(f); | 2676 values.push_back(f); |
2645 } | 2677 } |
2646 } | 2678 } |
2647 | 2679 |
2648 visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); | 2680 action = visitor.VisitIntegers(parentTags, parentIndexes, tag, vr, values); |
2649 break; | 2681 break; |
2650 } | 2682 } |
2651 | 2683 |
2652 case EVR_FL: // float single-precision | 2684 case EVR_FL: // float single-precision |
2653 case EVR_OF: | 2685 case EVR_OF: |
2664 { | 2696 { |
2665 values.push_back(f); | 2697 values.push_back(f); |
2666 } | 2698 } |
2667 } | 2699 } |
2668 | 2700 |
2669 visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); | 2701 action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); |
2670 break; | 2702 break; |
2671 } | 2703 } |
2672 | 2704 |
2673 case EVR_FD: // float double-precision | 2705 case EVR_FD: // float double-precision |
2674 #if DCMTK_VERSION_NUMBER >= 361 | 2706 #if DCMTK_VERSION_NUMBER >= 361 |
2687 { | 2719 { |
2688 values.push_back(f); | 2720 values.push_back(f); |
2689 } | 2721 } |
2690 } | 2722 } |
2691 | 2723 |
2692 visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); | 2724 action = visitor.VisitDoubles(parentTags, parentIndexes, tag, vr, values); |
2693 break; | 2725 break; |
2694 } | 2726 } |
2695 | 2727 |
2696 | 2728 |
2697 /** | 2729 /** |
2714 values.push_back(t); | 2746 values.push_back(t); |
2715 } | 2747 } |
2716 } | 2748 } |
2717 | 2749 |
2718 assert(vr == ValueRepresentation_AttributeTag); | 2750 assert(vr == ValueRepresentation_AttributeTag); |
2719 visitor.VisitAttributes(parentTags, parentIndexes, tag, values); | 2751 action = visitor.VisitAttributes(parentTags, parentIndexes, tag, values); |
2720 break; | 2752 break; |
2721 } | 2753 } |
2722 | 2754 |
2723 | 2755 |
2724 /** | 2756 /** |
2726 * "element.isLeaf()". | 2758 * "element.isLeaf()". |
2727 **/ | 2759 **/ |
2728 | 2760 |
2729 case EVR_SQ: // sequence of items | 2761 case EVR_SQ: // sequence of items |
2730 { | 2762 { |
2731 return; | 2763 return true; |
2732 } | 2764 } |
2733 | 2765 |
2734 | 2766 |
2735 /** | 2767 /** |
2736 * Internal to DCMTK. | 2768 * Internal to DCMTK. |
2749 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image | 2781 case EVR_pixelSQ: // used internally for pixel sequences in a compressed image |
2750 case EVR_pixelItem: // used internally for pixel items in a compressed image | 2782 case EVR_pixelItem: // used internally for pixel items in a compressed image |
2751 case EVR_PixelData: // used internally for uncompressed pixeld data | 2783 case EVR_PixelData: // used internally for uncompressed pixeld data |
2752 case EVR_OverlayData: // used internally for overlay data | 2784 case EVR_OverlayData: // used internally for overlay data |
2753 { | 2785 { |
2754 visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); | 2786 action = visitor.VisitNotSupported(parentTags, parentIndexes, tag, vr); |
2755 return; | 2787 break; |
2756 } | 2788 } |
2757 | 2789 |
2758 | 2790 |
2759 /** | 2791 /** |
2760 * Default case. | 2792 * Default case. |
2761 **/ | 2793 **/ |
2762 | 2794 |
2763 default: | 2795 default: |
2764 return; | 2796 return true; |
2797 } | |
2798 | |
2799 switch (action) | |
2800 { | |
2801 case ITagVisitor::Action_None: | |
2802 return true; // We're done | |
2803 | |
2804 case ITagVisitor::Action_Remove: | |
2805 return false; | |
2806 | |
2807 case ITagVisitor::Action_Replace: | |
2808 throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace non-string-like data"); | |
2809 | |
2810 default: | |
2811 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
2765 } | 2812 } |
2766 } | 2813 } |
2767 catch (boost::bad_lexical_cast&) | 2814 catch (boost::bad_lexical_cast&) |
2768 { | 2815 { |
2769 return; | 2816 return true; |
2770 } | 2817 } |
2771 catch (std::bad_cast&) | 2818 catch (std::bad_cast&) |
2772 { | 2819 { |
2773 return; | 2820 return true; |
2774 } | 2821 } |
2775 } | 2822 } |
2776 | 2823 |
2777 | 2824 |
2778 static void ApplyVisitorToElement(DcmElement& element, | 2825 // Returns "true" iff the element must be kept. If "false" is |
2826 // returned, the element will be removed. | |
2827 static bool ApplyVisitorToElement(DcmElement& element, | |
2779 ITagVisitor& visitor, | 2828 ITagVisitor& visitor, |
2780 const std::vector<DicomTag>& parentTags, | 2829 const std::vector<DicomTag>& parentTags, |
2781 const std::vector<size_t>& parentIndexes, | 2830 const std::vector<size_t>& parentIndexes, |
2782 Encoding encoding, | 2831 Encoding encoding, |
2783 bool hasCodeExtensions) | 2832 bool hasCodeExtensions) |
2786 | 2835 |
2787 DicomTag tag(FromDcmtkBridge::Convert(element.getTag())); | 2836 DicomTag tag(FromDcmtkBridge::Convert(element.getTag())); |
2788 | 2837 |
2789 if (element.isLeaf()) | 2838 if (element.isLeaf()) |
2790 { | 2839 { |
2791 ApplyVisitorToLeaf(element, visitor, parentTags, parentIndexes, tag, encoding, hasCodeExtensions); | 2840 return ApplyVisitorToLeaf(element, visitor, parentTags, parentIndexes, tag, encoding, hasCodeExtensions); |
2792 } | 2841 } |
2793 else | 2842 else |
2794 { | 2843 { |
2795 // "All subclasses of DcmElement except for DcmSequenceOfItems | 2844 // "All subclasses of DcmElement except for DcmSequenceOfItems |
2796 // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset | 2845 // are leaf nodes, while DcmSequenceOfItems, DcmItem, DcmDataset |
2797 // etc. are not." The following dynamic_cast is thus OK. | 2846 // etc. are not." The following dynamic_cast is thus OK. |
2798 DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(element); | 2847 DcmSequenceOfItems& sequence = dynamic_cast<DcmSequenceOfItems&>(element); |
2799 | 2848 |
2800 if (sequence.card() == 0) | 2849 if (sequence.card() == 0) |
2801 { | 2850 { |
2802 visitor.VisitEmptySequence(parentTags, parentIndexes, tag); | 2851 ITagVisitor::Action action = visitor.VisitEmptySequence(parentTags, parentIndexes, tag); |
2852 | |
2853 switch (action) | |
2854 { | |
2855 case ITagVisitor::Action_None: | |
2856 return true; | |
2857 | |
2858 case ITagVisitor::Action_Remove: | |
2859 return false; | |
2860 | |
2861 case ITagVisitor::Action_Replace: | |
2862 throw OrthancException(ErrorCode_NotImplemented, "Iterator cannot replace sequences"); | |
2863 | |
2864 default: | |
2865 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
2866 } | |
2803 } | 2867 } |
2804 else | 2868 else |
2805 { | 2869 { |
2806 std::vector<DicomTag> tags = parentTags; | 2870 std::vector<DicomTag> tags = parentTags; |
2807 std::vector<size_t> indexes = parentIndexes; | 2871 std::vector<size_t> indexes = parentIndexes; |
2812 { | 2876 { |
2813 indexes.back() = static_cast<size_t>(i); | 2877 indexes.back() = static_cast<size_t>(i); |
2814 DcmItem* child = sequence.getItem(i); | 2878 DcmItem* child = sequence.getItem(i); |
2815 ApplyVisitorToDataset(*child, visitor, tags, indexes, encoding, hasCodeExtensions); | 2879 ApplyVisitorToDataset(*child, visitor, tags, indexes, encoding, hasCodeExtensions); |
2816 } | 2880 } |
2881 | |
2882 return true; // Keep | |
2817 } | 2883 } |
2818 } | 2884 } |
2819 } | 2885 } |
2820 | 2886 |
2821 | 2887 |