I need to make a conversion from a DICOM image to a JPG/PNG and save the image using VTK, but the image that I produce does not match the original.
I know I need rescaling the pixels of the image to convert it but I do not know how. Does anyone know how I can do the conversion properly?
Below, my code in python:
from vtk import *
reader = vtkDICOMImageReader()
reader.SetFileName('image.dcm')
reader.Update()
castFilter = vtkImageCast()
castFilter.SetOutputScalarTypeToUnsignedChar()
castFilter.SetInputConnection(reader.GetOutputPort())
castFilter.Update()
writer = vtkJPEGWriter()
writer.SetFileName('output.jpg')
writer.SetInputConnection(castFilter.GetOutputPort())
writer.Write()
DICOMs in MRI and CT modalities are generally short types, and you are casting the image to unsigned char mercilessly.
If you are trying to get a corresponding uchar image, you should be using vtkImageShiftScale, just like the vtkImageCast docs say:
Warning
As vtkImageCast only casts values without rescaling them, its use is not recommented. vtkImageShiftScale is the recommented way to
change the type of an image data.
I made the conversion, here is my code:
from vtk import vtkDICOMImageReader
from vtk import vtkImageShiftScale
from vtk import vtkPNGWriter
reader = vtkDICOMImageReader()
reader.SetFileName('image.dcm')
reader.Update()
image = reader.GetOutput()
shiftScaleFilter = vtkImageShiftScale()
shiftScaleFilter.SetOutputScalarTypeToUnsignedChar()
shiftScaleFilter.SetInputConnection(reader.GetOutputPort())
shiftScaleFilter.SetShift(-1.0*image.GetScalarRange()[0])
oldRange = image.GetScalarRange()[1] - image.GetScalarRange()[0]
newRange = 255
shiftScaleFilter.SetScale(newRange/oldRange)
shiftScaleFilter.Update()
writer = vtkPNGWriter()
writer.SetFileName('output.jpg')
writer.SetInputConnection(shiftScaleFilter.GetOutputPort())
writer.Write()
Related
There is very limited documentation on using this method with C++. Most of the documentation is for VB. Please help me to 1) create a range object 2) use this range object with the AddPicture() method.
Here is the AddPicture definition for C++:
LPDISPATCH InlineShapes::AddPicture(LPCTSTR FileName, VARIANT* LinkToFile, VARIANT* SaveWithDocument, VARIANT* Range)
Below is working code that inserts an image into a word document. It inserts at top of doc because the range parameter(4th parameter, currently 'covOptional') is not specified. There is other code that sets up m_disp to interact with document of interest.
_Document objDoc;
COleVariant covOptional;
//instantiate the document object
objDoc.AttachDispatch(m_disp);
//adding image to doc
InlineShapes objInlineShapes(objDoc.GetInlineShapes())
objInlineShapes.AddPicture("C:\\QR.png", covOptional, covOptional, covOptional);
Here is more info on what I am trying to do incase there are alternative ways. I have a word document that I need to add a png image to. I see a couple ways of doing this: 1) hardcode range objects that specify the position in the document of the png to be inserted into 2) add anchor strings (ex. %pngLocation%) to the document. Find a way to return a range that represents this string's location. Use that range with the AddPicture() method.
I had to use a different msword library, but I got this to work with the following code.
#import "C:\Program Files (x86)\Microsoft Office\root\Office16\MSWORD.OLB" named_guids raw_native_types rename("ExitWindows", "WordExitWindows") rename("FindText", "WordFindText"), rename("VBE", "testVBE")
#include "path\debug\msword.tlh"
//setting up
Word::WindowPtr pWindow = w_app.GetActiveWindow();
Word::Range* pRange = pWindow->Selection->GetRange();
pRange->Start = 20;
pRange->End = 20;
VARIANT vTargetRange;
vTargetRange.vt = VT_DISPATCH;
vTargetRange.pdispVal = pRange;
I was able to use '&vTargetRange' as the range parameter in AddPicture().
Thank you to Castorix31.
I'm using Sony camera (model: RX100-VI)
I process the ARW files (whose I get as outputsfrom the camera) using the package "RawPy".
after erforming the next lines I get numpy image:
import rawpy as rp
r1 = rp.imread("DSC00025.ARW")
im1 = r1.raw_image_visible
I want to know if the filter array is Bayer. Do Ih have any way to ensure it?
Thanks =]
I want to use C++ to load TensorFlow model. And I want to know size of model's input, which is the placeholder in the model.
I google this problem, but I just find this link in stackoverflow :
C++ equivalent of python: tf.Graph.get_tensor_by_name() in Tensorflow?
Although I can get node, but tensorflow document don't tell me how to access the size of the node. So is there anyone know something about this?
Thank you so much!
OK,after many times attempts. I have find a workaround solution, It maybe tricky but works well.
At first, we can get the placeholder node using following code:
GraphDef mygd = graph_def.graph_def();
for (int i = 0; i < mygd.node_size(); i++)
{
if (mygd.node(i).name() == input_name)
{
auto node = mygd.node(i);
}
}
Then through the NodeDef.pd.h(tensorflow/core/framework/node_def.pb.h), we can get AttrValue through code like below:
auto attr = node.attr();
Then through the attr_value.cc(tensorflow/core/framework/attr_value.cc), we can get the shape attr value through code like below:
tensorflow::AttrValue shape = attr["shape"];
and the shape AttrValue is the structure used to store shape information. We can get the detail information through the function SummarizeAttrValue in tensorflow/core/framework/attr_value_util.h
string size_summary = SummarizeAttrValue(shape);
And then we can get the string format of shape like below:
[?,1024]
At this moment, i'm working in a shapefile visor in C++ and QT and using the GDAL/OGR library. I have this method to get the EPSG of my shapefiles:
OGRLayer layer = dataset->GetLayer(0);
OGRSpatialReference *spatialRef = layer->GetSpatialRef();
With this I get the EPSG number with:
atoi(spatialRef->GetAuthorityCode(NULL));
This work fine in all my shape files less one. In this case, the method always retun null.
I try use:
spatialRef->GetAuthorityCode("PROJCS");
spatialRef->GetAuthorityCode("GEOGCS");
spatialRef->GetAuthorityName("GEOGCS");
And all this method return "".
I check this shapefile in a gis program as QGIS and QGIS autodetected that his EPSG is 25830.
My question is this: could the projection information be readed with a different method than what I'm doing?
I wait yours suggestions.
Thank a lot.
EDIT
This is the content of .prj file:
PROJCS["ETRS89_UTM_zone_30N",GEOGCS["GCS_ETRS_1989",DATUM["D_ETRS_1989",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",-3],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["Meter",1]]
Something like this should work:
OGRLayer * layer = dataset->GetLayer(0);
layer->ResetReading();
OGRFeature * feat= layer->GetNextFeature();
OGRGeometry * geom = feat->GetGeometryRef();
OGRSpatialReference * spatRef = geom->getSpatialReference();
int EPSG = spatRef->GetEPSGGeogCS();
Hope it helps!
I have a google-cloud-ml model that I can run prediction by passing a 3 dimensional array of float32...
{ 'instances' [ { 'input' : '[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]' } ] }
However this is not an efficient format to transmit images, so I'd like to pass base64 encoded png or jpeg. This document talks about doing that, but what is not clear is what the entire json object looks like. Does the { 'b64' : 'x0welkja...' } go in place of the '[ [ [ 0.0 ], [ 0.5 ], [ 0.8 ] ] ... ] ]', leaving the enclosing 'instances' and 'input' the same? Or some other structure? Or does the tensorflow model have to be trained on base64?
The TensorFlow model does not have to be trained on base64 data. Leave your training graph as is. However, when exporting the model, you'll need to export a model that can accept PNG or jpeg (or possibly raw, if it's small) data. Then, when you export the model, you'll need to be sure to use a name for the output that ends in _bytes. This signals to CloudML Engine that you will be sending base64 encoded data. Putting it all together would like something like this:
from tensorflow.contrib.saved_model.python.saved_model import utils
# Shape of [None] means we can have a batch of images.
image = tf.placeholder(shape = [None], dtype = tf.string)
# Decode the image.
decoded = tf.image.decode_jpeg(image, channels=3)
# Do the rest of the processing.
scores = build_model(decoded)
# The input name needs to have "_bytes" suffix.
inputs = { 'image_bytes': image }
outputs = { 'scores': scores }
utils.simple_save(session, export_dir, inputs, outputs)
The request you send will look something like this:
{
"instances": [{
"b64": "x0welkja..."
}]
}
If you just want an efficient way to send images to a model (and not necessarily base-64 encode it), I would suggest uploading your images(s) to Google Cloud Storage and then having your model read off GCS. This way, you are not limited by image size and you can take advantage of multi-part, multithreaded, resumable uploads etc. that the GCS API provides.
TensorFlow's tf.read_file will directly off GCS. Here's an example of a serving input_fn that will do this. Your request to CMLE would send it an image URL (gs://bucket/some/path/to/image.jpg)
def read_and_preprocess(filename, augment=False):
# decode the image file starting from the filename
# end up with pixel values that are in the -1, 1 range
image_contents = tf.read_file(filename)
image = tf.image.decode_jpeg(image_contents, channels=NUM_CHANNELS)
image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
image = tf.expand_dims(image, 0) # resize_bilinear needs batches
image = tf.image.resize_bilinear(image, [HEIGHT, WIDTH], align_corners=False)
#image = tf.image.per_image_whitening(image) # useful if mean not important
image = tf.subtract(image, 0.5)
image = tf.multiply(image, 2.0) # -1 to 1
return image
def serving_input_fn():
inputs = {'imageurl': tf.placeholder(tf.string, shape=())}
filename = tf.squeeze(inputs['imageurl']) # make it a scalar
image = read_and_preprocess(filename)
# make the outer dimension unknown (and not 1)
image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])
features = {'image' : image}
return tf.estimator.export.ServingInputReceiver(features, inputs)
Your training code will train off actual images, just as in rhaertel80's suggestion above. See https://github.com/GoogleCloudPlatform/training-data-analyst/blob/master/courses/machine_learning/deepdive/08_image/flowersmodel/trainer/task.py#L27 for what the training/evaluation input functions would look like.
I was trying to use #Lak's answer (thanks Lak) to get online predictions for multiple instances in one json file, but kept getting the following error (I had two instances in my test json, hence the shape [2]):
input filename tensor must be scalar but had shape [2]
The problem is that ML engine apparently batches all the instances together and passes them to the serving inpur receiver function, but #Lak's sample code assumes the input is a single instance (it indeed works fine if you have a single instance in your json). I altered the code so that it can process a batch of inputs. I hope it will help someone:
def read_and_preprocess(filename):
image_contents = tf.read_file(filename)
image = tf.image.decode_image(image_contents, channels=NUM_CHANNELS)
image = tf.image.convert_image_dtype(image, dtype=tf.float32) # 0-1
return image
def serving_input_fn():
inputs = {'imageurl': tf.placeholder(tf.string, shape=(None))}
filename = inputs['imageurl']
image = tf.map_fn(read_and_preprocess, filename, dtype=tf.float32)
# make the outer dimension unknown (and not 1)
image = tf.placeholder_with_default(image, shape=[None, HEIGHT, WIDTH, NUM_CHANNELS])
features = {'image': image}
return tf.estimator.export.ServingInputReceiver(features, inputs)
The key changes are that 1) you don't squeeze the input tensor (that would cause trouble in the special case when your json contains only one instance) and, 2) use tf.map_fn to apply the read_and_preprocess function to a batch of input image urls.