[vtkusers] No LookupTable was set but input data is not VTK_UNSIGNED_CHAR

David Gobbi david.gobbi at gmail.com
Thu May 19 13:07:23 EDT 2016


You said "I can not use vtkImageMapToColors without LUT, because the
application is crashing" but you posted code, and an output image that the
code produced.  So I am confused.  Did the code crash?  Or did it just
display an error message?


I'll try my best to give an explanation of how the VTK 3D image display
pipeline works.

The vtkImageActor can, by itself, apply a window/level to the image.  To
set this window/level, use these methods:

m_pImageActor->GetProperty()->SetColorWindow(w);
m_pImageActor->GetProperty()->SetColorLevel(l);

By default, the window/level for vtkImageActor are set to window=255.0,
level=127.5. This window and level provide a range of [0.0, 255.0].  This
is the full display range for 8-bits.  Converting between "window, level"
and "range" is simple mathematics, and is described in many books and on
many web pages, e.g.
http://docs.oracle.com/cd/E19253-01/817-7519/6mmr6chkl/index.html

When you have vtkImageMapToColors in your pipeline, it will take your CT
data (16 bits) as input, and it will produce RGBA (8-bits per component)
data as output.  But it can only do this if you give it a lookup table.  If
you do not give a lookup table, then it will display an error message, and
it will output the original CT data, and then the vtkImageActor will apply
its own default window/level of window=255.0, level=127.5.  This
window/level does not come from the data!  It is simply the default
window/level for vtkImageActor.

When you give a LUT to vtkImageMapToColors, it produces RGBA data as output
(with 8-bits per component).  The vtkImageActor then applies its default
window/level of 255.0,127.5 but note that, for 8-bit data, this
window/level is an identity mapping!  A value of zero maps to zero, and a
value of 255 maps to 255.  This means the mapping is controlled completely
by the range of the LUT used with vtkImageMapToColors.  The vtkImageActor
doesn't change the values at all.


So, now let us consider the two output images that you posted.  In the
first one, it looks like you set the range of the LUT to [-5, 75], based on
the fact that the image says (W: 80, L: 35).  As I said in my previous
email, you will have to tell me where those numbers came from!  Using (W:
80, L: 35) only makes sense if you want to look at the brain, it is of
course useless for bone because it saturates the bone (that is, it makes
the bone completely white as you described).  So a doctor will use this
window/level if they are looking for a brain injury such as a hemorrhage.
If they are interested in looking at bone injury, they will start with
something like (W: 3000, L: 1000).

For the second image you posted (the one with no LUT) the vtkImageActor is
applying its default (W: 255.0, L: 127.5) which is a large window than (W:
80, L: 35) but is definitely smaller than you want.


So I'll ask yet again... where did you get the values 80, 35?  From the
DICOM meta data?  If so, they are obviously valid values (as I said, they
are what a radiologist might use for investigating brain injury) even if
they aren't the values that you want to use.

 - David


On Thu, May 19, 2016 at 8:41 AM, David Gobbi <david.gobbi at gmail.com> wrote:

> And the values (W: 80, L: 35) that are shown on the image... where do they
> come from?
>
> On Thu, May 19, 2016 at 7:30 AM, Flaviu2 <flaviu2 at yahoo.com> wrote:
>
>> David, I cannot thank you enough for your kindness.
>> Yes, I post the code used for case without LUT:
>>
>>
>>     vtkDICOMReader* m_pDICOMReader = vtkDICOMReader::New();
>>     m_pDICOMReader->RemoveAllInputs();
>>     vtkDICOMApplyRescale* m_pRescale = vtkDICOMApplyRescale::New();
>>     m_pDICOMReader->SetFileNames(pFN); // all files from serie
>>     m_pDICOMReader->AutoRescaleOff();
>>     m_pDICOMReader->Update();
>>     m_pRescale->SetInputConnection(m_pDICOMReader->GetOutputPort());
>>     m_pRescale->Update();
>>     vtkImageReslice* m_pResliceAxial = vtkImageReslice::New();
>>     m_pResliceAxial->SetInputConnection(m_pRescale->GetOutputPort());
>>     vtkImageMapToColors* m_pColorAxial = vtkImageMapToColors::New();
>>     m_pColorAxial->SetInputConnection(m_pResliceAxial->GetOutputPort());
>>     vtkImageActor* m_pImageActor = vtkImageActor::New();
>>     // and then, forward, setup m_pColorAxial to an image actor:
>>
>> pImageActor->GetMapper()->SetInputConnection(m_pColorAxial->GetOutputPort());
>>     // and add image actor to a renderer
>>     m_pRenderer->AddActor(m_pImageActor);
>>
>> In fact, I am using the same code, but I am not using vtkLookupTable at
>> all ... I know, it is not quite ok, but it is the only way to have the
>> original window level from the image ...
>>
>> Flaviu.
>>
>>
>>
>>
>>
>>
>> On Thursday, May 19, 2016 4:03 PM, David Gobbi <david.gobbi at gmail.com>
>> wrote:
>>
>>
>> I will try to answer your question, but I need to know more about your
>> code first.  Can you post the code you used for the "Snapshot_without_LUT"
>> image?  In particular, the screenshot has a Window/Level annotation (W: 80,
>> L: 35), so show me how those values are used in the code.
>>
>> On Thu, May 19, 2016 at 6:35 AM, Flaviu2 <flaviu2 at yahoo.com> wrote:
>>
>> Hi David. Something strange is happen ... I had tried to set LUT as you
>> said, but the image are still whiter than without LUT ... see attached
>> images ...
>>
>> And of course, I can not use vtkImageMapToColors without LUT, because the
>> application is crashing ... is there a solution here ?
>>
>> Flaviu.
>>
>>
>>
>>
>>
>> Hi Flaviu,
>>
>> This is incorrect:
>>
>> m_pLUTAxial->SetRange(dLevel, dWindow);
>>
>> It should be something like this:
>>
>> m_pLUTAxial->SetRange(dLevel - 0.5*dWindow, dLevel + 0.5*dWindow);
>>
>>  - David
>>
>>
>> On Fri, May 13, 2016 at 1:22 AM, Flaviu2 <flaviu2 at yahoo.com> wrote:
>>
>> This is a little pseudocode of the entire pipeline:
>>
>> vtkDICOMReader* m_pDICOMReader = vtkDICOMReader::New();
>> m_pDICOMReader->RemoveAllInputs();
>> vtkDICOMApplyRescale* m_pRescale = vtkDICOMApplyRescale::New();
>> m_pDICOMReader->SetFileNames(pFN); // all files from serie
>> m_pDICOMReader->AutoRescaleOff();
>> m_pDICOMReader->Update();
>> m_pRescale->SetInputConnection(m_pDICOMReader->GetOutputPort());
>> m_pRescale->Update();
>> vtkImageReslice* m_pResliceAxial = vtkImageReslice::New();
>> m_pResliceAxial->SetInputConnection(m_pRescale->GetOutputPort());
>> vtkLookupTable* m_pLUTAxial = vtkLookupTable::New();
>> m_pLUTAxial->SetValueRange(0.0, 1.0); // from black to white
>> m_pLUTAxial->SetSaturationRange(0.0, 0.0); // no color saturation
>> vtkDICOMMetaData* pMeta = m_pDICOMReader->GetMetaData();
>> const vtkDICOMValue& window = pMeta->GetAttributeValue(DC::WindowWidth);
>> const vtkDICOMValue& level = pMeta->GetAttributeValue(DC::WindowCenter);
>> double dWindow = window.AsDouble();
>> double dLevel = level.AsDouble();
>> m_pLUTAxial->SetRange(dLevel, dWindow); // image intensity range
>> m_pLUTAxial->SetRampToLinear();
>> m_pLUTAxial->Build();
>> vtkImageMapToColors* m_pColorAxial = vtkImageMapToColors::New();
>> m_pColorAxial->SetLookupTable(m_pLUTAxial);
>> m_pColorAxial->SetInputConnection(m_pResliceAxial->GetOutputPort());
>> vtkImageActor* m_pImageActor = vtkImageActor::New();
>> // and then, forward, setup m_pColorAxial to an image actor:
>>
>> pImageActor->GetMapper()->SetInputConnection(m_pColorAxial->GetOutputPort());
>> // and add image actor to a renderer
>> m_pRenderer->AddActor(m_pImageActor);
>>
>>
>> I had taken "Window center" and "Window width" and put them as range on
>> lookuptable ... the image are little dark, and is not like the original
>> (without setup lookup table looks good, but I get those warning like in
>> post subject) ... might not understand well what you said about this range,
>> but I am feel I am not far from solve this problem ... can you guide me a
>> little bit ?
>>
>> Flaviu.
>>
>>
>>
>>
>>
>> Since you're already using vtk-dicom, you can use vtkDICOMApplyRescale
>> to convert your CT image to Hounsfield units (if you use this filter, you
>> should
>> also call AutoRescaleOff() on the reader).
>>
>> After the image has been converted to Hounsfield units, you can use the
>> "Window Center" and "Window Width" presets that are stored in the meta
>> data to set the range for the lookup table.
>>
>> However, I usually ignore these presets, and instead use the VTK class
>> vtkImageHistogramStatistics to compute the range.  Its GetAutoRange()
>> computes a range by doing some simple histogram analysis.
>>
>> If you enjoy doing a lot of reading, the relevant parts the DICOM standard
>> are here:
>>
>> http://dicom.nema.org/MEDICAL/Dicom/current/output/chtml/part03/sect_C.11.html
>>
>> After the image goes through vtkImageMapToColors, how are you
>> rendering it?
>>
>>  - David
>>
>>
>>
>>
>>
>>
>>
>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/vtkusers/attachments/20160519/9d0f368a/attachment.html>


More information about the vtkusers mailing list