Skip to content

Commit

Permalink
WIP: Start preparing CF simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
Fredrik Orderud committed Mar 23, 2020
1 parent 3c63511 commit e7a3c14
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 24 deletions.
68 changes: 56 additions & 12 deletions DummyLoader/Image3dSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,37 @@ Image3dSource::Image3dSource() {
m_ecg = EcgSeries(ecg);
}
{
// flat gray scale
for (size_t i = 0; i < m_color_map.size(); ++i)
m_color_map[i] = R8G8B8A8(static_cast<unsigned char>(i), static_cast<unsigned char>(i), static_cast<unsigned char>(i), 0xFF);
// flat gray tissue scale
for (size_t i = 0; i < m_color_map_tissue.size(); ++i)
m_color_map_tissue[i] = R8G8B8A8(static_cast<unsigned char>(i), static_cast<unsigned char>(i), static_cast<unsigned char>(i), 0xFF);
}
{
// red to blue flow scale with green at high BW
assert(m_color_map_flow.size() == 256*256);
for (size_t bw = 0; bw < 256; ++bw) {
// increasing green for high bandwidth
uint8_t green = (bw >= 128) ? static_cast<unsigned char>(bw) : 0;

for (size_t freq = 0; freq < 256; ++freq) { // unsigned counter, so freq>127 corresponds to negative velocities
// increasing red for positive velocities
uint8_t red = (freq < 128) ? 128+static_cast<unsigned char>(freq) : 0;
// increasing blue for negative velocities
uint8_t blue = (freq >= 128) ? 128+static_cast<unsigned char>(255-freq) : 0;

m_color_map_flow[freq + bw*256] = R8G8B8A8(red, green, blue, 0xFF);
}
}
}
{
// flow arbitration scale
assert(m_flow_arb.size() == 256*256);
for (size_t freq = 0; freq < 256; ++freq) { // unsigned counter, so freq>127 corresponds to negative velocities
for (size_t bw = 0; bw < 256; ++bw) {
// show flow when |freq| >= 32
bool show_flow = std::abs(static_cast<int>(freq)) >= 32;
m_flow_arb[bw + freq*256] = show_flow ? 0xFF : 0x00;
}
}
}
{
// image geometry X Y Z
Expand All @@ -46,8 +74,9 @@ Image3dSource::Image3dSource() {
m_bbox = geom;
}

// a single tissue stream
// simulate tissue + color-flow data
m_stream_types.push_back(IMAGE_TYPE_TISSUE);
m_stream_types.push_back(IMAGE_TYPE_BLOOD_VEL);
}

Image3dSource::~Image3dSource() {
Expand Down Expand Up @@ -96,15 +125,30 @@ HRESULT Image3dSource::GetColorMap(ColorMapType type, /*out*/ImageFormat * forma
if (*map)
return E_INVALIDARG;

if (type != TYPE_TISSUE_COLOR)
return E_NOT_SET;
if (type == TYPE_TISSUE_COLOR) {
*format = IMAGE_FORMAT_R8G8B8A8;
// copy to new buffer
CComSafeArray<uint8_t> color_map(4*static_cast<unsigned int>(m_color_map_tissue.size()));
memcpy(&color_map.GetAt(0), m_color_map_tissue.data(), sizeof(m_color_map_tissue));
*map = color_map.Detach(); // transfer ownership
return S_OK;
} else if (type == TYPE_FLOW_COLOR) {
*format = IMAGE_FORMAT_R8G8B8A8;
// copy to new buffer
CComSafeArray<uint8_t> color_map(4*static_cast<unsigned int>(m_color_map_flow.size()));
memcpy(&color_map.GetAt(0), m_color_map_flow.data(), sizeof(m_color_map_flow));
*map = color_map.Detach(); // transfer ownership
return S_OK;
} else if (type = TYPE_FLOW_ARB) {
*format = IMAGE_FORMAT_U8;
// copy to new buffer
CComSafeArray<uint8_t> color_map(static_cast<unsigned int>(m_flow_arb.size()));
memcpy(&color_map.GetAt(0), m_flow_arb.data(), sizeof(m_flow_arb));
*map = color_map.Detach(); // transfer ownership
return S_OK;
}

*format = IMAGE_FORMAT_R8G8B8A8;
// copy to new buffer
CComSafeArray<uint8_t> color_map(4*static_cast<unsigned int>(m_color_map.size()));
memcpy(&color_map.GetAt(0), m_color_map.data(), sizeof(m_color_map));
*map = color_map.Detach(); // transfer ownership
return S_OK;
return E_NOTIMPL;
}

HRESULT Image3dSource::GetECG(/*out*/EcgSeries *ecg) {
Expand Down
4 changes: 3 additions & 1 deletion DummyLoader/Image3dSource.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class ATL_NO_VTABLE Image3dSource :
private:
ProbeInfo m_probe;
EcgSeries m_ecg;
std::array<R8G8B8A8,256> m_color_map;
std::array<R8G8B8A8,256> m_color_map_tissue;
std::array<R8G8B8A8,256*256> m_color_map_flow;
std::array<uint8_t,256*256> m_flow_arb;

Cart3dGeom m_bbox = {};
std::vector<ImageType> m_stream_types;
Expand Down
25 changes: 24 additions & 1 deletion DummyLoader/Image3dStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ void Image3dStream::Initialize (ImageType type, Cart3dGeom img_geom, Cart3dGeom
const double duration = 1.0; // Seconds
const double startTime = 10.0;

{
if (type == IMAGE_TYPE_TISSUE) {
// checker board image data
unsigned short dims[] = { 20, 15, 10 }; // matches length of dir1, dir2 & dir3, so that the image squares become quadratic
std::vector<byte> img_buf(dims[0] * dims[1] * dims[2]);
Expand Down Expand Up @@ -55,6 +55,29 @@ void Image3dStream::Initialize (ImageType type, Cart3dGeom img_geom, Cart3dGeom

m_frames.push_back(CreateImage3d(frameNumber*(duration/numFrames) + startTime, IMAGE_FORMAT_U8, dims, img_buf));
}
} else if (type == IMAGE_TYPE_BLOOD_VEL) {
// sine wave velocity data
unsigned short dims[] = { 20, 15, 10 }; // matches length of dir1, dir2 & dir3, so that the image squares become quadratic
std::vector<byte> img_buf(2 * dims[0] * dims[1] * dims[2]);
for (size_t frameNumber = 0; frameNumber < numFrames; ++frameNumber) {
for (unsigned int z = 0; z < dims[2]; ++z) {
for (unsigned int y = 0; y < dims[1]; ++y) {
for (unsigned int x = 0; x < dims[0]; ++x) {
double angle = x*2*M_PI/dims[0]; // in [0, 2*pi)
angle += frameNumber*2*M_PI/numFrames; // in [0, 4*pi)

int8_t & freq = reinterpret_cast<int8_t&>(img_buf[0 + 2*(x + y*dims[0] + z*dims[0] * dims[1])]);
byte & bw = img_buf[1 + 2*(x + y*dims[0] + z*dims[0] * dims[1])];

freq = static_cast<int8_t>(255*(0.5f - y*1.0f/dims[1])); // [+127, -128] along Y axis
bw = static_cast<uint8_t>(256*(x*1.0f/dims[0])); // [0,255] along X axis
}
}
}
m_frames.push_back(CreateImage3d(frameNumber*(duration/numFrames) + startTime, IMAGE_FORMAT_FREQ8POW8, dims, img_buf));
}
} else {
abort(); // should never be reached
}
}

Expand Down
78 changes: 68 additions & 10 deletions TestViewer/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ public partial class MainWindow : Window
IImage3dSource m_source;

IImage3dStream m_streamXY;
IImage3dStream m_streamXYcf;
IImage3dStream m_streamXZ;
IImage3dStream m_streamXZcf;
IImage3dStream m_streamZY;
IImage3dStream m_streamZYcf;

public MainWindow()
{
Expand All @@ -69,8 +72,11 @@ void ClearUI()
ImageZY.Source = null;

m_streamXY = null;
m_streamXYcf = null;
m_streamXZ = null;
m_streamXZcf = null;
m_streamZY = null;
m_streamZYcf = null;

ECG.Data = null;

Expand Down Expand Up @@ -302,6 +308,8 @@ private void InitializeSlices()
bboxXY.dir3_y = 0;
bboxXY.dir3_z = 0;
m_streamXY = m_source.GetStream(0, bboxXY, new ushort[] { HORIZONTAL_RES, VERTICAL_RES, 1 });
if (stream_count >= 2)
m_streamXYcf = m_source.GetStream(1, bboxXY, new ushort[] { HORIZONTAL_RES, VERTICAL_RES, 1 }); // assume 2nd stream is color-flow

// get XZ plane (assumes 1st axis is "X" and 3rd is "Z")
Cart3dGeom bboxXZ = bbox;
Expand All @@ -315,6 +323,8 @@ private void InitializeSlices()
bboxXZ.dir3_y = 0;
bboxXZ.dir3_z = 0;
m_streamXZ = m_source.GetStream(0, bboxXZ, new ushort[] { HORIZONTAL_RES, VERTICAL_RES, 1 });
if (stream_count >= 2)
m_streamXZcf = m_source.GetStream(1, bboxXZ, new ushort[] { HORIZONTAL_RES, VERTICAL_RES, 1 }); // assume 2nd stream is color-flow

// get ZY plane (assumes 2nd axis is "Y" and 3rd is "Z")
Cart3dGeom bboxZY = bbox;
Expand All @@ -331,37 +341,75 @@ private void InitializeSlices()
bboxZY.dir3_y = 0;
bboxZY.dir3_z = 0;
m_streamZY = m_source.GetStream(0, bboxZY, new ushort[] { HORIZONTAL_RES, VERTICAL_RES, 1 });
if (stream_count >= 2)
m_streamZYcf = m_source.GetStream(1, bboxZY, new ushort[] { HORIZONTAL_RES, VERTICAL_RES, 1 }); // assume 2nd stream is color-flow
}

private void DrawSlices (uint frame)
{
Debug.Assert(m_source != null);

// retrieve image slices
uint cf_frame = 0;
if (m_streamXYcf != null) {
// find closest corresponding CF frame
double t_time = m_streamXY.GetFrame(frame).time;
double[] cf_times = m_streamXYcf.GetFrameTimes();

int closest_idx = 0;
for (int i = 1; i < cf_times.Length; ++i) {
if (Math.Abs(cf_times[i] - t_time) < Math.Abs(cf_times[closest_idx] - t_time))
closest_idx = i;
}

cf_frame = (uint)closest_idx;
}

// get XY plane (assumes 1st axis is "X" and 2nd is "Y")
Image3d imageXY = m_streamXY.GetFrame(frame);
Image3d imageXYcf = new Image3d();
if (m_streamXYcf != null)
imageXYcf = m_streamXYcf.GetFrame(cf_frame);

// get XZ plane (assumes 1st axis is "X" and 3rd is "Z")
Image3d imageXZ = m_streamXZ.GetFrame(frame);
Image3d imageXZcf = new Image3d();
if (m_streamXZcf != null)
imageXZcf = m_streamXZcf.GetFrame(cf_frame);


// get ZY plane (assumes 2nd axis is "Y" and 3rd is "Z")
Image3d imageZY = m_streamZY.GetFrame(frame);
Image3d imageZYcf = new Image3d();
if (m_streamZYcf != null)
imageZYcf = m_streamZYcf.GetFrame(cf_frame);

FrameTime.Text = "Frame time: " + imageXY.time;

ImageFormat image_format;
byte[] color_map = m_source.GetColorMap(ColorMapType.TYPE_TISSUE_COLOR, out image_format);
byte[] tissue_map = m_source.GetColorMap(ColorMapType.TYPE_TISSUE_COLOR, out image_format);
if (image_format != ImageFormat.IMAGE_FORMAT_R8G8B8A8)
throw new Exception("Unexpected color-map format");

byte[] cf_map = m_source.GetColorMap(ColorMapType.TYPE_FLOW_COLOR, out image_format);
if (image_format != ImageFormat.IMAGE_FORMAT_R8G8B8A8)
throw new Exception("Unexpected color-map format");

ImageXY.Source = GenerateBitmap(imageXY, color_map);
ImageXZ.Source = GenerateBitmap(imageXZ, color_map);
ImageZY.Source = GenerateBitmap(imageZY, color_map);
byte[] arb_table = m_source.GetColorMap(ColorMapType.TYPE_FLOW_ARB, out image_format);
if (image_format != ImageFormat.IMAGE_FORMAT_U8)
throw new Exception("Unexpected color-map format");


ImageXY.Source = GenerateBitmap(imageXY, imageXYcf, tissue_map, cf_map, arb_table);
ImageXZ.Source = GenerateBitmap(imageXZ, imageXZcf, tissue_map, cf_map, arb_table);
ImageZY.Source = GenerateBitmap(imageZY, imageZYcf, tissue_map, cf_map, arb_table);
}

private WriteableBitmap GenerateBitmap(Image3d t_img, byte[] t_map)
private WriteableBitmap GenerateBitmap(Image3d t_img, Image3d cf_img, byte[] t_map, byte[] cf_map, byte[] arb_table)
{
Debug.Assert(t_img.dims.SequenceEqual(cf_img.dims));
Debug.Assert(t_img.format == ImageFormat.IMAGE_FORMAT_U8);
Debug.Assert(cf_img.format == ImageFormat.IMAGE_FORMAT_FREQ8POW8);

WriteableBitmap bitmap = new WriteableBitmap(t_img.dims[0], t_img.dims[1], 96.0, 96.0, PixelFormats.Rgb24, null);
bitmap.Lock();
unsafe
Expand All @@ -371,8 +419,10 @@ private WriteableBitmap GenerateBitmap(Image3d t_img, byte[] t_map)
for (int x = 0; x < bitmap.Width; ++x)
{
byte t_val = t_img.data[x + y * t_img.stride0];
ushort cf_val = BitConverter.ToUInt16(cf_img.data, 2*x + y*(int)cf_img.stride0);

byte* pixel = (byte*)bitmap.BackBuffer + x * (bitmap.Format.BitsPerPixel / 8) + y * bitmap.BackBufferStride;
SetRGBVal(pixel, t_val, t_map);
SetRGBVal(pixel, t_val, cf_val, t_map, cf_map, arb_table);
}
}
}
Expand All @@ -381,10 +431,18 @@ private WriteableBitmap GenerateBitmap(Image3d t_img, byte[] t_map)
return bitmap;
}

unsafe static void SetRGBVal(byte* pixel, byte t_val, byte[] t_map)
unsafe static void SetRGBVal(byte* pixel, byte t_val, ushort cf_val, byte[] t_map, byte[] cf_map, byte[] arb_table)
{
// split input rgba color into individual channels
byte[] channels = BitConverter.GetBytes(BitConverter.ToUInt32(t_map, 4*t_val));
uint rgba = 0;
if (arb_table[cf_val] > t_val) {
// display color-flow overlay
rgba = BitConverter.ToUInt32(cf_map, 4*cf_val);
} else {
// display tissue underlay
rgba = BitConverter.ToUInt32(t_map, 4*t_val);
}

byte[] channels = BitConverter.GetBytes(rgba);
// assign red, green & blue
pixel[0] = channels[0]; // red
pixel[1] = channels[1]; // green
Expand Down

0 comments on commit e7a3c14

Please sign in to comment.