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