trackedussweepframerenderer3d.cpp 13 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// ================================================================================================
// 
// This file is part of the CAMPVis Software Framework.
// 
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
//      Chair for Computer Aided Medical Procedures
//      Technische Universitt Mnchen
//      Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
// 
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
// 
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// 
// ================================================================================================

#include "trackedussweepframerenderer3d.h"
#include "tgt/bounds.h"
#include "tgt/logmanager.h"
#include "tgt/shadermanager.h"
#include "tgt/texture.h"
#include "tgt/textureunit.h"

#include "core/datastructures/imagedata.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/datastructures/imagerepresentationrendertarget.h"

#include "core/datastructures/meshgeometry.h"
#include "core/datastructures/facegeometry.h"

#include "core/classification/simpletransferfunction.h"

#include "core/tools/quadrenderer.h"

#include "modules/scr_msk/datastructures/usinterfacedata.h"
#include <usinterface/include/trackedusfileio.h>
#include <usinterface/include/trackedusframe.h>
#include <usinterface/include/trackedussweep.h>

schultezub's avatar
schultezub committed
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

namespace {
    struct AverageTopCornersAccessFunc {
        vct3 operator() (const TrackedUSSweep::CornerUnion* corners, size_t index) {
            return (corners[index]._topLeftCorner + corners[index]._topRightCorner) * 0.5;
        }
    };

    template<class AccessFunc, int KERNEL_SIZE>
    struct GaussianSmoothingFunc {
        AccessFunc a;
        double kernel[KERNEL_SIZE + 1];
        double sigma;

        GaussianSmoothingFunc() {
            sigma = static_cast<double>(KERNEL_SIZE) / 2.5; // rough estimate

            for (size_t i = 0; i <= KERNEL_SIZE; ++i) {
                double f = static_cast<float>(i);
                kernel[i] = exp(-(f*f) / (2.0 * sigma * sigma));
            }
        }

        vct3 operator() (const TrackedUSSweep::CornerUnion* corners, size_t index, size_t start, size_t end) {
            vct3 toReturn(0.0);
            double norm = 0.0;
            for (int offset = -KERNEL_SIZE; offset <= KERNEL_SIZE; ++offset) {
                int i = static_cast<int>(index) + offset;
                if (i >= start && i < end) {
                    toReturn += a(corners, i) * kernel[abs(offset)];
                    norm += kernel[abs(offset)];
                }
            }

            toReturn /= norm;
            return toReturn;
        }
    };
}

93
94
95
96
97
98
99
100
101
102
namespace campvis {
    const std::string TrackedUsSweepFrameRenderer3D::loggerCat_ = "CAMPVis.modules.vis.TrackedUsSweepFrameRenderer3D";

    TrackedUsSweepFrameRenderer3D::TrackedUsSweepFrameRenderer3D(IVec2Property& canvasSize)
        : VisualizationProcessor(canvasSize)
        , p_sourceImageID("sourceFioID", "Input Tracked US File IO", "", DataNameProperty::READ, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_PROPERTIES)
        , p_targetImageID("targetImageID", "Output Image", "", DataNameProperty::WRITE)
        , p_camera("Camera", "Camera")
        , p_sweepNumber("sweepNumber", "SweepNumber", 0, 0, 0, AbstractProcessor::INVALID_RESULT | AbstractProcessor::INVALID_PROPERTIES)
        , p_frameNumber("sliceNumber", "Slice Number", 0, 0, 0)
schultezub's avatar
schultezub committed
103
104
        , p_showConfidenceMap("ShowConfidenceMap", "Show Confidence Map", false)
        , p_smoothButton("SmoothButton", "Smooth Tracking")
105
106
107
108
109
110
111
112
113
        , p_transferFunction("transferFunction", "Transfer Function", new SimpleTransferFunction(256))
        , _shader(0)
        , _currentSweep(0)
    {
        addProperty(&p_sourceImageID);
        addProperty(&p_targetImageID);
        addProperty(&p_camera);
        addProperty(&p_sweepNumber);
        addProperty(&p_frameNumber);
schultezub's avatar
schultezub committed
114
115
        addProperty(&p_showConfidenceMap);
        addProperty(&p_smoothButton);
116
        addProperty(&p_transferFunction);
schultezub's avatar
schultezub committed
117

118
119
120
121
122
123
124
125
126
    }

    TrackedUsSweepFrameRenderer3D::~TrackedUsSweepFrameRenderer3D() {

    }

    void TrackedUsSweepFrameRenderer3D::init() {
        VisualizationProcessor::init();
        _shader = ShdrMgr.loadSeparate("core/glsl/passthrough.vert", "modules/scr_msk/glsl/trackedussweepframerenderer3d.frag", "", false);
schultezub's avatar
schultezub committed
127
        p_smoothButton.s_clicked.connect(this, &TrackedUsSweepFrameRenderer3D::onSmoothButtonClicked);
128
129
130
    }

    void TrackedUsSweepFrameRenderer3D::deinit() {
schultezub's avatar
schultezub committed
131
        p_smoothButton.s_clicked.disconnect(this);
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
        VisualizationProcessor::deinit();
        ShdrMgr.dispose(_shader);
        delete _currentSweep;
    }

    void TrackedUsSweepFrameRenderer3D::process(DataContainer& data) {
        DataContainer::ScopedTypedData< TrackedUsFileIoData > fio(data, p_sourceImageID.getValue());

        if (fio != 0) {
            if (hasInvalidProperties()) {
                updateProperties(const_cast<TrackedUSFileIO*>(fio->getData()));
            }

            if (_currentSweep != 0) {
                int frameNr = p_frameNumber.getValue();
                const tgt::Camera& cam = p_camera.getValue();

                // ultra ugly and dirty hacking:
                std::vector<tgt::vec3> corners(
                    reinterpret_cast<const tgt::dvec3*>(_currentSweep->getCorner(frameNr)._corners), 
                    reinterpret_cast<const tgt::dvec3*>(_currentSweep->getCorner(frameNr)._corners) + 4);

                std::vector<tgt::vec3> texCoords;
schultezub's avatar
schultezub committed
155
156
                texCoords.push_back(tgt::vec3(0.f, 1.f, 0.f)); // swapped top/bottom texture coordinates to
                texCoords.push_back(tgt::vec3(1.f, 1.f, 0.f)); // comply with mirrored y axis in OpenGL
157
                texCoords.push_back(tgt::vec3(1.f, 0.f, 0.f));
schultezub's avatar
schultezub committed
158
                texCoords.push_back(tgt::vec3(0.f, 0.f, 0.f));
159
160

                FaceGeometry slice(corners, texCoords);
schultezub's avatar
schultezub committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
                MeshGeometry bb = MeshGeometry::createCube(_bounds, tgt::Bounds(tgt::vec3(-1.f), tgt::vec3(-1.f)));

                const unsigned char* tmp = (p_showConfidenceMap.getValue() ? _currentSweep->getConfidenceMap(frameNr) : _currentSweep->getTrackedUSFrame(frameNr)->getImageBuffer());
                if (tmp != 0) {
                    std::pair<ImageData*, ImageRepresentationRenderTarget*> rt = ImageRepresentationRenderTarget::createWithImageData(_renderTargetSize.getValue());

                    glPushAttrib(GL_ALL_ATTRIB_BITS);
                    glEnable(GL_DEPTH_TEST);
                    _shader->activate();
                    _shader->setIgnoreUniformLocationError(true);
                    _shader->setUniform("_viewportSizeRCP", 1.f / tgt::vec2(_renderTargetSize.getValue()));
                    _shader->setUniform("_projectionMatrix", cam.getProjectionMatrix());
                    _shader->setUniform("_viewMatrix", cam.getViewMatrix());

                    tgt::TextureUnit inputUnit, tfUnit;
                    inputUnit.activate();

                    tgt::Texture tex(
                        const_cast<unsigned char*>(tmp), 
                        tgt::vec3(_currentSweep->Width(), _currentSweep->Height(), 1), 
                        GL_ALPHA,
                        GL_ALPHA8,
                        GL_UNSIGNED_BYTE, 
                        tgt::Texture::LINEAR);
                    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
                    tex.setType(GL_TEXTURE_2D);
                    tex.bind();
                    tex.uploadTexture();
                    tex.setWrapping(tgt::Texture::CLAMP);
                    tex.setPixelData(0);

                    _shader->setUniform("_texture", inputUnit.getUnitNumber());
                    _shader->setUniform("_textureParameters._size", tgt::vec2(_currentSweep->Width(), _currentSweep->Height()));
                    _shader->setUniform("_textureParameters._sizeRCP", tgt::vec2(1.f) / tgt::vec2(_currentSweep->Width(), _currentSweep->Height()));
                    _shader->setUniform("_textureParameters._numChannels", 1);

                    p_transferFunction.getTF()->bind(_shader, tfUnit);

                    rt.second->activate();
                    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
                    _shader->setAttributeLocation(0, "in_Position");
                    _shader->setAttributeLocation(1, "in_TexCoord");

                    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                    bb.render();
                    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

                    slice.render();
                    rt.second->deactivate();

                    _shader->setIgnoreUniformLocationError(false);
                    _shader->deactivate();
                    tgt::TextureUnit::setZeroUnit();
                    glPopAttrib();

                    data.addData(p_targetImageID.getValue(), rt.first);
                    p_targetImageID.issueWrite();
                }
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
            }
        }
        else {
            LERROR("No suitable input image found.");
        }

        validate(INVALID_RESULT);
    }

    void TrackedUsSweepFrameRenderer3D::updateProperties(TrackedUSFileIO* fio) {
        if (fio != 0) {
            const TrackedUSFileXMLHeader::TrackedUSFileStudy *study = fio->getStudyHeader(0);
            p_sweepNumber.setMaxValue(static_cast<int>(study->Series.size()) - 1);

            int seriesno = p_sweepNumber.getValue();

            if (seriesno < study->Series.size()) {
                delete _currentSweep;
                fio->unmap();


                bool retval = fio->map(seriesno);
                if (retval) {
                    int totalFrames=study->Series[seriesno]->BytesAvailable/study->Series[seriesno]->FrameSize;
                    p_frameNumber.setMaxValue(totalFrames - 1);

                    _currentSweep = new TrackedUSSweep(study->Series[seriesno]->Width,
                        study->Series[seriesno]->Height, 
                        totalFrames, 
                        fio->getData(), 
                        study->Series[seriesno]->Tracking[0]->CalibrationMatrix(),
                        study->Series[seriesno]->Tracking[0]->RegistrationMatrix(),
                        fio->getConfidenceMap());

                    _currentSweep->SetPixelSize(study->Series[seriesno]->PixelSizeX, study->Series[seriesno]->PixelSizeY);
                    _currentSweep->CalculateCornersAndPose();
                    _currentSweep->CalculatePrincipalAxes();

schultezub's avatar
schultezub committed
257
258
259
260
261
262
263
264
265
                    if (_currentSweep->getConfidenceMap() == 0) {
                        p_showConfidenceMap.setValue(false);
                        p_showConfidenceMap.setVisible(false);
                    }
                    else {
                        p_showConfidenceMap.setVisible(true);
                    }

                    updateBoundingBox();
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
                }
                else {
                    _currentSweep = 0;
                    LERROR("Failed to map sweep " + seriesno);
                    return;
                }
            }
        }

        validate(AbstractProcessor::INVALID_PROPERTIES);
    }

    const TrackedUSSweep* TrackedUsSweepFrameRenderer3D::getCurrentSweep() const {
        return _currentSweep;
    }

schultezub's avatar
schultezub committed
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
    void TrackedUsSweepFrameRenderer3D::onSmoothButtonClicked() {
        if (_currentSweep != 0) {
            _currentSweep->SmoothCorners(AverageTopCornersAccessFunc(), GaussianSmoothingFunc<AverageTopCornersAccessFunc, 16>());
            updateBoundingBox();
            invalidate(AbstractProcessor::INVALID_RESULT);
        }
    }

    void TrackedUsSweepFrameRenderer3D::updateBoundingBox() {
        tgt::dvec3 llf, urb;
        _currentSweep->BoundingBox(llf.elem, urb.elem);
        _bounds = tgt::Bounds(llf, urb);
        s_boundingBoxChanged(_bounds);
    }

297
}