<p>Ok after searching a whole lot on the internet I found a <a href="https://humanhealth.iaea.org/HHW/MedicalPhysics/TheMedicalPhysicist/Studentscorner/HandbookforTeachersandStudents/Chapter_11.pdf" rel="nofollow noreferrer">slide</a> which answered most of my questions and doubts. I'm dumping it here in-case anybody else is curious what these datasets usually contain and what should be (in my opinion) the correct way to handle mappings.</p>

<p>So apparently the CT scanner always records the densities of the substances in Hounsfield Scale. The Hounsfield Scale itself is a little interesting. For body organs and stuff, the scale has the range from -1024 to 3000. This was the scale I talked about earlier as when normalized the values usually lie between 0-4096. However metals usually go past this 3k boundary and some metals like stainless steel and gold having HU number as high as 13k and 30k respectively. This is usually called the "extended" Hounsfield Scale.
Some volumetric datasets contain data for computer simulations mostly fluids etc. For these type of datasets either density or flow velocity is captured. Precision is important for these datasets hence <code>float</code> is used.</p>

<p>Hence the <code>uint8</code> datasets are probably grey-scale data converted from the original HU units. <code>uint16</code> datasets contain the original Hounsfield data while <code>float</code> may contain density or velocity. </p>

<p>Now for the mapping part, as others stated one way might be to just divide by the max value of the data type or the max value of the data itself, to get the <code>uint16</code> or <code>float</code> data in <code>0-1</code> range then multiply by <code>255</code> to get a grey-scale image. However doing so may lower the contrast and hide minute details. For example, dividing a <code>uint16</code> dataset that contained only HU units in range <code>300-2000</code> with the <span class="math-container">$2^{16}$</span> will map the HU units to only a small range of grey values. Dividing by <code>2000</code> in this case is surely better. However after reading how CT-scanners work, it's better to let the user set the minimum and maximum range of Hounsfield units they wish to observe. This is usually called the <strong>window width</strong>. And then map this window width to the whole <code>0-255</code> range of grey levels as YardenJ2R mentioned in his second formula. This is more in line with how radiologists visualise the data. </p>

<p>For fancy transfer functions which I haven't implemented yet, we might have to convert the Hounsfield units to opacity or grey scale as mentioned above then map that opacity to color or there might be a way to directly map it to colors using a LUT as defined by the user's control point settings.</p>

CC BY-SA 4.0
2019-11-15T09:51:20.147Z
2019-11-15T09:51:20.147Z
gallickgunner
<p>It seems you want to work with values between 0-255. For uint16 datasets, you can put it inside 0-255 range like this:</p>

<p><span class="math-container">$newValue = 255 * \frac{oldValue}{(2^{16} - 1)}$</span></p>

<p>where <span class="math-container">$oldValue$</span> is an uint16 value and <span class="math-container">$2^{16} - 1$</span> is the uint16 max number. For the float ones without any previous knowledge about its range, you could read all the values from the dataset in order to find out the min and the max values. Then, you could map this way:</p>

<p><span class="math-container">$newValue = 255 * \frac{oldValue - min}{max - min}$</span></p>

<p>The key idea is to map first to range 0-1 and then map it to 0-255 by multiplying it by 255. Don't forget to round/ceil/floor your value as you want an integer, not a float number.</p>

CC BY-SA 4.0
2019-11-12T15:55:32.517Z
I think you are misunderstanding the situation probably. Firstly although it's a `uint16` dataset, the values are in the range `0-4095` which I guess are in Hounsfield units. For a simple remapping you should be dividing by 4095 instead of $2^{16}$. Again this seems more like just refitting the data range which I thought of initially but posted the question since I was thinking of a specific mapping operation which depends on what those values represent initially. Again my speculation can be wrong and if you really are sure about it then I'll wait for other answers
gallickgunner
CC BY-SA 4.0
2019-11-12T16:10:40.927Z
Yes, in this case you should divide it by 4095. I don't see why you shouldn't use this mapping. Perhaps, you would need to do a previous one. I don't know this Hounsfield units, but suppose you would like to work with cm instead of meters. The dataset is uint16 with range 0-4095 in meters. Then, first you would have to multiply $oldValue$ by 100 (this is just an example). Although, as you said, I might be misunderstanding the situation.
YardenJ2R
CC BY-SA 4.0
2019-11-12T15:30:47.943Z
2019-11-12T15:30:47.943Z
YardenJ2R
<p>So I've written a personal volume renderer which currently visualizes datasets (CT-Scans mostly) that have a <code>.raw</code> extension and the values are in the range <code>0-255</code> or <code>uint8</code>.</p>

<p>Now I searched the net and found some datasets that had <code>uint16</code> (ranging from <code>0-4095</code>) and <code>float</code> which was arbitrary but went from like <code>0-20</code> or something. I think the <code>uint16</code> dataset corresponds to values in Hounsfield scale but I'm unsure of how do I map these to 0-255.</p>

<p>I haven't implemented any fancy transfer functions currently. For the <code>uint8</code> datasets I just read the isovalues from the dataset and use that value for all the channels (r,g,b) as it is giving me a grayscale image.</p>

<p>EDIT:- To clarify further, I have implemented a gpu based ray marching algorithm taking help from this site <a href="http://graphicsrunner.blogspot.com/2009/01/volume-rendering-101.html" rel="nofollow noreferrer">here</a>, I'm taking the datasets from <a href="https://klacansky.com/open-scivis-datasets/" rel="nofollow noreferrer">here</a>, As mentioned in the comments I don't really know how you should interpret these values, but a quick read shows that normally the CT data is based on Hounsfield units which represents density afaik.</p>

CC BY-SA 4.0
2019-11-11T18:27:55.020Z
Could you be more specific? What exactly are you asking?
ivokabel
CC BY-SA 4.0
2019-11-11T18:41:56.867Z
Simply stating I want to know how to interpret values outside 0-255 range? How do i map them to 0-255
gallickgunner
CC BY-SA 4.0
2019-11-11T21:30:51.303Z
What are you using the 0-255 values for? Density?
lightxbulb
CC BY-SA 4.0
2019-11-12T11:21:22.107Z
Yes, I think. Are there other ways to use those values? I don't really have much knowledge about it since there is so little on the internet regarding volume rendering especially one related to scientific visualization. The sites from where I get these datasets don't tell what unit are the values supposed to be in or how to interpret them. Updated the post with the sites.
gallickgunner
CC BY-SA 4.0
2019-11-12T16:15:33.770Z
I would suggest remapping both the `uint8` and `uint16` version to [0,1]. Then you can multiply by some factor if necessary. More precisely: `v= v / max_v`.
lightxbulb
<volumetric><volume-rendering>
How to handle different data types (uint8, uint16, float) in Volume Datasets?
gallickgunner
# How to handle different data types (uint8, uint16, float) in Volume Datasets?

(Asked by: username_0 on 2019-11-11)

So I've written a personal volume renderer which currently visualizes datasets (CT-Scans mostly) that have a `.raw` extension and the values are in the range `0-255` or `uint8`.

Now I searched the net and found some datasets that had `uint16` (ranging from `0-4095`) and `float` which was arbitrary but went from like `0-20` or something. I think the `uint16` dataset corresponds to values in Hounsfield scale but I'm unsure of how do I map these to 0-255.

I haven't implemented any fancy transfer functions currently. For the `uint8` datasets I just read the isovalues from the dataset and use that value for all the channels (r,g,b) as it is giving me a grayscale image.

EDIT:- To clarify further, I have implemented a gpu based ray marching algorithm taking help from this site [here](http://graphicsrunner.blogspot.com/2009/01/volume-rendering-101.html), I'm taking the datasets from [here](https://klacansky.com/open-scivis-datasets/), As mentioned in the comments I don't really know how you should interpret these values, but a quick read shows that normally the CT data is based on Hounsfield units which represents density afaik.

**Tags:** volumetric, volume-rendering

> **Comments:**
> **username_1**: Could you be more specific? What exactly are you asking?  
> **username_0**: Simply stating I want to know how to interpret values outside 0-255 range? How do i map them to 0-255  
> **username_2**: What are you using the 0-255 values for? Density?  
> **username_0**: Yes, I think. Are there other ways to use those values? I don't really have much knowledge about it since there is so little on the internet regarding volume rendering especially one related to scientific visualization. The sites from where I get these datasets don't tell what unit are the values supposed to be in or how to interpret them. Updated the post with the sites.  
> **username_2**: I would suggest remapping both the `uint8` and `uint16` version to [0,1]. Then you can multiply by some factor if necessary. More precisely: `v= v / max_v`.  

---

## 2 answers

### Answer by username_0 on 2019-11-15. Score: 2 | Accepted answer

Ok after searching a whole lot on the internet I found a [slide](https://humanhealth.iaea.org/HHW/MedicalPhysics/TheMedicalPhysicist/Studentscorner/HandbookforTeachersandStudents/Chapter_11.pdf) which answered most of my questions and doubts. I'm dumping it here in-case anybody else is curious what these datasets usually contain and what should be (in my opinion) the correct way to handle mappings.

So apparently the CT scanner always records the densities of the substances in Hounsfield Scale. The Hounsfield Scale itself is a little interesting. For body organs and stuff, the scale has the range from -1024 to 3000. This was the scale I talked about earlier as when normalized the values usually lie between 0-4096. However metals usually go past this 3k boundary and some metals like stainless steel and gold having HU number as high as 13k and 30k respectively. This is usually called the "extended" Hounsfield Scale.
Some volumetric datasets contain data for computer simulations mostly fluids etc. For these type of datasets either density or flow velocity is captured. Precision is important for these datasets hence `float` is used.

Hence the `uint8` datasets are probably grey-scale data converted from the original HU units. `uint16` datasets contain the original Hounsfield data while `float` may contain density or velocity.

Now for the mapping part, as others stated one way might be to just divide by the max value of the data type or the max value of the data itself, to get the `uint16` or `float` data in `0-1` range then multiply by `255` to get a grey-scale image. However doing so may lower the contrast and hide minute details. For example, dividing a `uint16` dataset that contained only HU units in range `300-2000` with the $2^{16}$ will map the HU units to only a small range of grey values. Dividing by `2000` in this case is surely better. However after reading how CT-scanners work, it's better to let the user set the minimum and maximum range of Hounsfield units they wish to observe. This is usually called the **window width**. And then map this window width to the whole `0-255` range of grey levels as username_3 mentioned in his second formula. This is more in line with how radiologists visualise the data.

For fancy transfer functions which I haven't implemented yet, we might have to convert the Hounsfield units to opacity or grey scale as mentioned above then map that opacity to color or there might be a way to directly map it to colors using a LUT as defined by the user's control point settings.


---

### Answer by username_3 on 2019-11-12. Score: 1

It seems you want to work with values between 0-255. For uint16 datasets, you can put it inside 0-255 range like this:

$newValue = 255 \* \frac{oldValue}{(2^{16} - 1)}$

where $oldValue$ is an uint16 value and $2^{16} - 1$ is the uint16 max number. For the float ones without any previous knowledge about its range, you could read all the values from the dataset in order to find out the min and the max values. Then, you could map this way:

$newValue = 255 \* \frac{oldValue - min}{max - min}$

The key idea is to map first to range 0-1 and then map it to 0-255 by multiplying it by 255. Don't forget to round/ceil/floor your value as you want an integer, not a float number.

> **Comments:**
> **username_0**: I think you are misunderstanding the situation probably. Firstly although it's a `uint16` dataset, the values are in the range `0-4095` which I guess are in Hounsfield units. For a simple remapping you should be dividing by 4095 instead of $2^{16}$. Again this seems more like just refitting the data range which I thought of initially but posted the question since I was thinking of a specific mapping operation which depends on what those values represent initially. Again my speculation can be wrong and if you really are sure about it then I'll wait for other answers  
> **username_3**: Yes, in this case you should divide it by 4095. I don't see why you shouldn't use this mapping. Perhaps, you would need to do a previous one. I don't know this Hounsfield units, but suppose you would like to work with cm instead of meters. The dataset is uint16 with range 0-4095 in meters. Then, first you would have to multiply $oldValue$ by 100 (this is just an example). Although, as you said, I might be misunderstanding the situation.  

---

