The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

imagerepresentationlocal.cpp 10.7 KB
Newer Older
1
2
3
4
// ================================================================================================
// 
// This file is part of the CAMPVis Software Framework.
// 
5
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
6
7
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
//      Chair for Computer Aided Medical Procedures
8
9
//      Technische Universität München
//      Boltzmannstr. 3, 85748 Garching b. München, Germany
10
// 
11
12
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
// 
13
14
15
16
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
17
// 
18
19
20
21
// Unless required by applicable law or agreed to in writing, software distributed under the 
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
22
23
24
25
26
// 
// ================================================================================================

#include "imagerepresentationlocal.h"

27
#include "tgt/glcontextmanager.h"
28
29
#include <tbb/tbb.h>
#include <tbb/spin_mutex.h>
30
#include "core/datastructures/imagerepresentationdisk.h"
31
#include "core/datastructures/imagerepresentationgl.h"
32
#include "core/datastructures/genericimagerepresentationlocal.h"
33
34
#include "core/tools/opengljobprocessor.h"
#include "core/tools/job.h"
35

36
37
38
39
#ifdef CAMPVIS_HAS_MODULE_ITK
#include "modules/itk/core/genericimagerepresentationitk.h"
#endif

40
41
42
43
44
45
#include <limits>

namespace campvis {
    
    const std::string ImageRepresentationLocal::loggerCat_ = "CAMPVis.core.datastructures.ImageRepresentationLocal";

46
    ImageRepresentationLocal::ImageRepresentationLocal(ImageData* parent, WeaklyTypedPointer::BaseType baseType)
47
        : GenericAbstractImageRepresentation<ImageRepresentationLocal>(parent)
48
49
50
51
52
53
54
55
56
57
        , _baseType(baseType)
        , _intensityHistogram(0)
    {
        _intensityRangeDirty = true;
    }

    ImageRepresentationLocal::~ImageRepresentationLocal() {
        delete _intensityHistogram;
    }

58
59
60
61
62
63
    ImageRepresentationLocal* ImageRepresentationLocal::tryConvertFrom(const AbstractImageRepresentation* source) {
        if (source == 0)
            return 0;

        // test source image type via dynamic cast
        if (const ImageRepresentationDisk* tester = dynamic_cast<const ImageRepresentationDisk*>(source)) {
64
            return create(tester->getParent(), tester->getImageData());
65
66
67
        }
        else if (const ImageRepresentationGL* tester = dynamic_cast<const ImageRepresentationGL*>(source)) {
            ImageRepresentationLocal* toReturn = 0;
68
69
70
71
72
73
74
75
76
77

            if (GLJobProc.isCurrentThreadOpenGlThread()) {
                try {
                    WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerCopy();
                    toReturn = create(source->getParent(), wtp);
                }
                catch (...) {
                    LERROR("An unknown error occured during conversion...");
                }

78
            }
79
80
81
82
83
84
85
86
87
88
89
90
            else {
                tgt::GLCanvas* context = GLJobProc.iKnowWhatImDoingGetArbitraryContext();
                GLJobProc.pause();
                try {
                    tgt::GLContextScopedLock lock(context);
                    WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerCopy();
                    toReturn = create(source->getParent(), wtp);
                }
                catch (...) {
                    LERROR("An unknown error occured during conversion...");
                }
                GLJobProc.resume();
91
92
            }
            return toReturn;
93
94
        }

95
96
97
98
99
100
#ifdef CAMPVIS_HAS_MODULE_ITK
        // There is no way to determine basetype, number of channels and dimensionality of
        // an ITK image at runtime. So there are currently 7*4*2 = 56 different possibilities
        // what source could be. Thank god, there exists macro magic to create the 56
        // different templated conversion codes.
#define CONVERT_ITK_TO_GENERIC_LOCAL(basetype, numchannels, dimensionality) \
101
102
        if (const GenericImageRepresentationItk<basetype, 1, 3>* tester = dynamic_cast< const GenericImageRepresentationItk<basetype, 1, 3>* >(source)) { \
            typedef GenericImageRepresentationItk<basetype, 1, 3>::ItkImageType ImageType; \
103
104
105
106
107
108
109
110
111
112
            typedef ImageType::PixelType PixelType; \
            const PixelType* pixelData = tester->getItkImage()->GetBufferPointer(); \
            \
            ImageType::RegionType region; \
            region = tester->getItkImage()->GetBufferedRegion(); \
            \
            ImageType::SizeType s = region.GetSize(); \
            tgt::svec3 size(s[0], s[1], s[2]); \
            \
            PixelType* pixelDataCopy = new PixelType[tgt::hmul(size)]; \
113
            memcpy(pixelDataCopy, pixelData, tgt::hmul(size) * TypeTraits<basetype, 1>::elementSize); \
114
            return GenericImageRepresentationLocal<PixelType, 1>::create(const_cast<ImageData*>(source->getParent()), pixelDataCopy); \
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        }

#define DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(numchannels, dimensionality) \
    CONVERT_ITK_TO_GENERIC_LOCAL(uint8_t, numchannels, dimensionality) \
    else CONVERT_ITK_TO_GENERIC_LOCAL(int8_t, numchannels, dimensionality) \
    else CONVERT_ITK_TO_GENERIC_LOCAL(uint16_t, numchannels, dimensionality) \
    else CONVERT_ITK_TO_GENERIC_LOCAL(int16_t, numchannels, dimensionality) \
    else CONVERT_ITK_TO_GENERIC_LOCAL(uint32_t, numchannels, dimensionality) \
    else CONVERT_ITK_TO_GENERIC_LOCAL(int32_t, numchannels, dimensionality) \
    else CONVERT_ITK_TO_GENERIC_LOCAL(float, numchannels, dimensionality)

#define DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_D(dimensionality) \
    DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(1, dimensionality) \
    else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(2, dimensionality) \
    else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(3, dimensionality) \
    else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(4, dimensionality) 

        // okay we've defined our macros. Now we just need to call them so that they call 
        // each other and create 56 different conversion checks - hooray
        DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_D(2)
        else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_D(3)
#endif

138
139
140
        return 0;
    }

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    const Interval<float>& ImageRepresentationLocal::getNormalizedIntensityRange() const {
        if (_intensityRangeDirty)
            computeNormalizedIntensityRange();

        return _normalizedIntensityRange;
    }

    const ConcurrentGenericHistogramND<float, 1>& ImageRepresentationLocal::getIntensityHistogram() const {
        if (_intensityHistogram == 0)
            computeIntensityHistogram();

        return *_intensityHistogram;
    }

    void ImageRepresentationLocal::computeNormalizedIntensityRange() const {
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
        _normalizedIntensityRange = Interval<float>(); // reset interval to empty one
        tbb::spin_mutex _mutex; // mutex to protect for concurrent access

        tbb::parallel_for(tbb::blocked_range<size_t>(0, getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
            float localMin = std::numeric_limits<float>::max();
            float localMax = -std::numeric_limits<float>::max();

            for (size_t i = range.begin(); i != range.end(); ++i) {
                float value = this->getElementNormalized(i, 0);
                localMax = std::max(localMax, value);
                localMin = std::min(localMin, value);
            }

            {
                // TODO: there is probably a more elegant method...
                tbb::spin_mutex::scoped_lock(mutex);
                _normalizedIntensityRange.nibble(localMin);
                _normalizedIntensityRange.nibble(localMax);
            }
        });

177
178
179
180
181
182
183
184
185
186
187
        _intensityRangeDirty = false;
    }

    void ImageRepresentationLocal::computeIntensityHistogram() const {
        delete _intensityHistogram;

        const Interval<float>& i = getNormalizedIntensityRange();
        float mins = i.getLeft();
        float maxs = i.getRight();
        size_t numBuckets = 1024;
        _intensityHistogram = new IntensityHistogramType(&mins, &maxs, &numBuckets);
188
189
190
191
192
193
        tbb::parallel_for(tbb::blocked_range<size_t>(0, getNumElements()), [&] (const tbb::blocked_range<size_t>& range) {
            for (size_t i = range.begin(); i != range.end(); ++i) {
                float value = this->getElementNormalized(i, 0);
                _intensityHistogram->addSample(&value);
            }
        });
194
195
    }

196
197
    
    ImageRepresentationLocal* ImageRepresentationLocal::create(const ImageData* parent, WeaklyTypedPointer wtp) {
198
#define CONVERT_DISK_TO_GENERIC_LOCAL(baseType,numChannels) \
199
        return GenericImageRepresentationLocal<baseType, numChannels>::create( \
200
            const_cast<ImageData*>(parent), \
201
202
            reinterpret_cast< TypeTraits<baseType, numChannels>::ElementType*>(wtp._pointer));

203
#define DISPATCH_DISK_TO_GENERIC_LOCAL_CONVERSION(numChannels) \
204
        if (parent->getNumChannels() == (numChannels)) { \
205
            switch (wtp._baseType) { \
206
                case WeaklyTypedPointer::UINT8: \
207
                    CONVERT_DISK_TO_GENERIC_LOCAL(uint8_t, (numChannels)) \
208
                case WeaklyTypedPointer::INT8: \
209
                    CONVERT_DISK_TO_GENERIC_LOCAL(int8_t, (numChannels)) \
210
                case WeaklyTypedPointer::UINT16: \
211
                    CONVERT_DISK_TO_GENERIC_LOCAL(uint16_t, (numChannels)) \
212
                case WeaklyTypedPointer::INT16: \
213
                    CONVERT_DISK_TO_GENERIC_LOCAL(int16_t, (numChannels)) \
214
                case WeaklyTypedPointer::UINT32: \
215
                    CONVERT_DISK_TO_GENERIC_LOCAL(uint32_t, (numChannels)) \
216
                case WeaklyTypedPointer::INT32: \
217
                    CONVERT_DISK_TO_GENERIC_LOCAL(int32_t, (numChannels)) \
218
                case WeaklyTypedPointer::FLOAT: \
219
                    CONVERT_DISK_TO_GENERIC_LOCAL(float, (numChannels)) \
220
221
222
223
224
225
                default: \
                    tgtAssert(false, "Should not reach this - wrong base data type!"); \
                    return 0; \
            } \
        }

226
227
228
229
        DISPATCH_DISK_TO_GENERIC_LOCAL_CONVERSION(1)
        else DISPATCH_DISK_TO_GENERIC_LOCAL_CONVERSION(2)
        else DISPATCH_DISK_TO_GENERIC_LOCAL_CONVERSION(3)
        else DISPATCH_DISK_TO_GENERIC_LOCAL_CONVERSION(4)
230
231
232
233
234
        else {
            tgtAssert(false, "Should not reach this - wrong number of channel!");
            return 0;
        }
    }
235
236


237
}