Vous avez reçu un message "Your GitLab account has been locked ..." ? Pas d'inquiétude : lisez cet article https://docs.gricad-pages.univ-grenoble-alpes.fr/help/unlock/

ResampleAction.cpp 10.3 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
5
 * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/
saubatn's avatar
saubatn committed
25
// CamiTK includes
26
#include "ResampleAction.h"
saubatn's avatar
saubatn committed
27
28
29
#include <Application.h>
#include <Property.h>

30
31
#include <Log.h>

saubatn's avatar
saubatn committed
32
// Vtk includes
33
#include <vtkCallbackCommand.h>
34
35
36
37
38
39
#include "vtkImageShiftScale.h"
#include <vtkImageResample.h>

using namespace camitk;

// --------------- Constructor -------------------
40
ResampleAction::ResampleAction(ActionExtension* extension) : Action(extension) {
41
42
    // Setting name, description and input component
    setName("Resample");
saubatn's avatar
saubatn committed
43
44
45
46
47
    setDescription("Resamples the input image in two different ways: \
                   <ul> \
                   <li>1. Resamples the scalar type of the voxels</li>\
                   <li>2. Resamples the size of the voxels</li> \
                   </ul>");
48
49
50
51
52
53
54
55
    setComponent("ImageComponent");

    // Setting classification family and tags
    setFamily("Image Processing");
    // Tags allow the user to search the actions through themes
    // You can add tags here with the method addTag("tagName");

    // Setting the actions parameters
56
    Property* newImageDimensionPropertyX = new Property(tr("New Image X Dimension"), 256, tr("The new image width (in voxels)."), "");
saubatn's avatar
saubatn committed
57
    newImageDimensionPropertyX->setAttribute("minimum", 1);
58
    newImageDimensionPropertyX->setAttribute("maximum", std::numeric_limits<int>::max());
saubatn's avatar
saubatn committed
59
60
61
    newImageDimensionPropertyX->setAttribute("singleStep", 1);
    addParameter(newImageDimensionPropertyX);

62
    Property* newImageDimensionPropertyY = new Property(tr("New Image Y Dimension"), 256, tr("The new image height (in voxels)."), "");
saubatn's avatar
saubatn committed
63
    newImageDimensionPropertyY->setAttribute("minimum", 1);
64
    newImageDimensionPropertyY->setAttribute("maximum", std::numeric_limits<int>::max());
saubatn's avatar
saubatn committed
65
66
67
    newImageDimensionPropertyY->setAttribute("singleStep", 1);
    addParameter(newImageDimensionPropertyY);

68
    Property* newImageDimensionPropertyZ = new Property(tr("New Image Z Dimension"), 256, tr("The new image depth (in voxels)."), "");
saubatn's avatar
saubatn committed
69
    newImageDimensionPropertyZ->setAttribute("minimum", 1);
70
    newImageDimensionPropertyZ->setAttribute("maximum", std::numeric_limits<int>::max());
saubatn's avatar
saubatn committed
71
72
73
    newImageDimensionPropertyZ->setAttribute("singleStep", 1);
    addParameter(newImageDimensionPropertyZ);

74
    Property* scalarTypeProperty = new Property(tr("New Image Scalar Type"), SAME_AS_INPUT, tr("The new image voxels scalar type"), "");
75
    scalarTypeProperty->setEnumTypeName("ScalarType", this);
saubatn's avatar
saubatn committed
76
    addParameter(scalarTypeProperty);
77
78
79
80
81
82
83
}

// --------------- destructor -------------------
ResampleAction::~ResampleAction() {

}

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// --------------- getWidget -------------------
QWidget* ResampleAction::getWidget() {
    
    // set min/max for the dimensions using the last selected target dimensions
    int * dims = getTargets().last()->getImageData()->GetDimensions();
    
    camitk::Property *dimProperty = getProperty("New Image X Dimension");
    dimProperty->setAttribute("minimum", 1);
    dimProperty->setAttribute("maximum", dims[0]);
    setProperty("New Image X Dimension", dims[0]);
    
    dimProperty = getProperty("New Image Y Dimension");
    dimProperty->setAttribute("minimum", 1);
    dimProperty->setAttribute("maximum", dims[1]);
    setProperty("New Image Y Dimension", dims[1]);

    dimProperty = getProperty("New Image Z Dimension");
    dimProperty->setAttribute("minimum", 1);
    dimProperty->setAttribute("maximum", dims[2]);
    setProperty("New Image Z Dimension", dims[2]);
    dimProperty->setReadOnly(dims[2] == 1);

    // return the default widget
    return Action::getWidget();
}
 
110
111
112
// --------------- apply -------------------
Action::ApplyStatus ResampleAction::apply() {

113
114
    foreach (Component* comp, getTargets()) {
        ImageComponent* input = dynamic_cast<ImageComponent*>(comp);
115
116
117
118
119
120
        process(input);
    }

    return SUCCESS;
}

121
// --------------- process -------------------
122
void ResampleAction::process(ImageComponent* comp) {
123
124
125
126
127
128
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
    Application::showStatusBarMessage(tr("Compute resample..."));
    Application::resetProgressBar();
    vtkSmartPointer<vtkCallbackCommand> progressCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    progressCallback->SetCallback(&Application::vtkProgressFunction);

129
    // Get the parameters
130
131
132
    double xSize = property("New Image X Dimension").toDouble();
    double ySize = property("New Image Y Dimension").toDouble();
    double zSize = property("New Image Z Dimension").toDouble();
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

    // Getting the input image
    vtkSmartPointer<vtkImageData> inputImage  = comp->getImageData();
    int dims[3];
    inputImage->GetDimensions(dims);


    /* Resamples the Scalar Type thanks to the shiftScaleFilter
       the shiftScaleFilter applies the following formula to each
       Voxel :
                           f(x)=a*(x+b)
       where b is the shift parameter, and a is the scale one.

       All the work is to compute the parameters according to
       the Scalar Type that the user wants.
       */

150
    double* imgRange = inputImage->GetScalarRange();
151
152
153
154
155
156
    double maxType, minType; // extrema values of the output Scalar Type
    double shift, scale;

    QString nameScalarType;

    vtkSmartPointer<vtkImageShiftScale> shiftScaleFilter = vtkSmartPointer<vtkImageShiftScale>::New();
157
158
159

    shiftScaleFilter->AddObserver(vtkCommand::ProgressEvent, progressCallback);

160
    shiftScaleFilter->SetInputData(inputImage);
161
162
163

    // Case on the input scalar type : compute the max and min type of the output
    // and also the name of the new image.
164
    switch (ResampleAction::getScalarType()) {
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
        case ResampleAction::UNSIGNED_CHAR:
            shiftScaleFilter->SetOutputScalarTypeToUnsignedChar();
            maxType = 255; // VTK_CHAR_MAX depends on the machine
            minType = 0.0; // same reason
            nameScalarType = "_UChar";
            break;
        case ResampleAction::CHAR:
            shiftScaleFilter->SetOutputScalarTypeToChar();
            maxType = VTK_SIGNED_CHAR_MAX;
            minType = VTK_SIGNED_CHAR_MIN;
            nameScalarType = "_Char";
            break;
        case ResampleAction::UNSIGNED_SHORT:
            shiftScaleFilter->SetOutputScalarTypeToUnsignedShort();
            maxType = VTK_UNSIGNED_SHORT_MAX;
            minType = VTK_UNSIGNED_SHORT_MIN;
            nameScalarType = "_UShort";
            break;
        case ResampleAction::SHORT:
            shiftScaleFilter->SetOutputScalarTypeToShort();
            maxType = VTK_SHORT_MAX;
            minType = VTK_SHORT_MIN;
            nameScalarType = "_Short";
            break;
        case ResampleAction::UNSIGNED_INT:
            shiftScaleFilter->SetOutputScalarTypeToUnsignedInt();
            maxType = VTK_UNSIGNED_INT_MAX;
            minType = VTK_UNSIGNED_INT_MIN;
            nameScalarType = "_UInt";
            break;
        case ResampleAction::INT:
            shiftScaleFilter->SetOutputScalarTypeToInt();
            maxType = VTK_INT_MAX;
            minType = VTK_INT_MIN;
            nameScalarType = "_Int";
            break;
        case ResampleAction::FLOAT:
            shiftScaleFilter->SetOutputScalarTypeToFloat();
            maxType = VTK_FLOAT_MAX;
            minType = VTK_FLOAT_MIN;
            nameScalarType = "_Float";
        case ResampleAction::DOUBLE:
            shiftScaleFilter->SetOutputScalarTypeToDouble();
            maxType = VTK_DOUBLE_MAX;
            minType = VTK_DOUBLE_MIN;
            nameScalarType = "_Double";
        default:
            break;
213
214
215
    }

    // Apply the shift scale filter
216
    if (ResampleAction::getScalarType() != SAME_AS_INPUT) {
217
218
        shift = (minType / (maxType - minType)) * (imgRange[1] - imgRange[0]) - imgRange[0];
        scale = (maxType - minType) / (imgRange[1] - imgRange[0]);
219
220
221
222
223
224
225
226
227
        shiftScaleFilter->SetShift(shift);
        shiftScaleFilter->SetScale(scale);
    }
    shiftScaleFilter->Update();

    /* Resamples the size of the voxels.

        It just has to compute the resize factor that is equal to the
        new size divided by the old size for each axe. */
228
229

    vtkSmartPointer <vtkImageResample> resampleFilter = vtkSmartPointer<vtkImageResample>::New();
230
231
232

    resampleFilter->AddObserver(vtkCommand::ProgressEvent, progressCallback);

233
234
235
    double xFactor = xSize / (double)dims[0];
    double yFactor = ySize / (double)dims[1];
    double zFactor = zSize / (double)dims[2];
236

237
    resampleFilter->SetInputData(shiftScaleFilter->GetOutput());
238
239
240
241
242
243
244
    resampleFilter->SetAxisMagnificationFactor(0, xFactor);
    resampleFilter->SetAxisMagnificationFactor(1, yFactor);
    resampleFilter->SetAxisMagnificationFactor(2, zFactor);

    resampleFilter->Update();

    // Create the output component
245
    ImageComponent* outputComp = new ImageComponent(resampleFilter->GetOutput(), comp->getName() + nameScalarType + "_" + property("New Image X Dimension").toString() + "x" + property("New Image Y Dimension").toString() + "x" + property("New Image Z Dimension").toString());
246

saubatn's avatar
saubatn committed
247
248
    // consider frame policy on new image created
    Action::applyTargetPosition(comp, outputComp);
249
250

    // refresh restore the normal cursor and progress bar
251
    Application::refresh();
252
253
254
    Application::resetProgressBar();
    Application::showStatusBarMessage("");
    QApplication::restoreOverrideCursor();
255
256
257

}

258
// --------------- getScalarType -------------------
saubatn's avatar
saubatn committed
259
ResampleAction::ScalarType ResampleAction::getScalarType() {
260
    return (ScalarType) property("New Image Scalar Type").toInt();
261
262
}