Giter Club home page Giter Club logo

Comments (8)

blowekamp avatar blowekamp commented on June 2, 2024

Do you have a minimal reproducible example of this issue?

Also any reason why not using the sitk.GetArrayFromImage method?

from simpleitk.

greg814 avatar greg814 commented on June 2, 2024

Minimal example:

import SimpleITK as sitk
img = sitk.Image()
img.GetBufferAsFloat()

Traceback (most recent call last):
AttributeError: 'Image' object has no attribute 'GetBufferAsFloat'

Reason: When working with many large image volumes in parallel on low-performance systems I want to allow suitable in-place numpy operations on the sitk.Image buffer without creating copies.

from simpleitk.

blowekamp avatar blowekamp commented on June 2, 2024

I am able to find in the git history any intentional removal of these methods in Python. Nor do I ever recall intentionally exposing them to go around the copy on write policies in Python. This will require more searching and maybe a git bisect to determine what changed the interface.

How are you converting the swig *float buffer to numpy?

There is the sitk::_GetMemoryViewFromImage method which is used by sitk::GetArrayFromImage, which returns a python memory view without performing the copy on write logics that may work as a replacement.

from simpleitk.

greg814 avatar greg814 commented on June 2, 2024

The following code works for me in SimpleITK version 2.2.1:

import ctypes
import numpy as np
import SimpleITK as sitk

def get_array_from_buffer(img):
	buffer_type = ctypes.c_float * img.GetNumberOfPixels()
	buffer      = buffer_type.from_address(int(img.GetBufferAsFloat()))
	np_arr      = np.frombuffer(buffer, dtype=np.float32)
	
	# reshape: sitk (x:columns, y:rows, z:slices) -> numpy (z:slices, y:rows, x:columns)
	return np_arr.reshape(img.GetDepth(), img.GetHeight(), img.GetWidth())

reader = sitk.ImageFileReader()
reader.SetFileName('path/to/source.dcm')
reader.SetOutputPixelType(sitk.sitkFloat32)    # convert to 32-bit float
img = reader.Execute()

arr = get_array_from_buffer(img)
print(arr, '\n\n')

arr += 1234
print(arr, '\n\n')

# replace pixel value and check if it happened in the buffer
arr[0,2,1] = 4321
print(img[1,2,0])

print(img.GetBufferAsFloat())
print(sitk._SimpleITK._GetMemoryViewFromImage(img))

The last 2 print statements return different things. How would I use the latter to work like the former?

from simpleitk.

blowekamp avatar blowekamp commented on June 2, 2024

I think this is the conversion you are looking for:

image_memory_view = _GetMemoryViewFromImage(image)
array_view = numpy.asarray(image_memory_view).view(dtype=dtype)
array_view.shape = shape[::-1]

from simpleitk.

greg814 avatar greg814 commented on June 2, 2024

Problem is numpy.view is readonly:

def get_array_from_buffer(img):
	image_memory_view = sitk._SimpleITK._GetMemoryViewFromImage(img)
	array_view = np.asarray(image_memory_view).view(dtype=np.float32)
	array_view[0] = 123
	
	# reshape: sitk (x:columns, y:rows, z:slices) -> numpy (z:slices, y:rows, x:columns)
	return array_view.reshape(img.GetDepth(), img.GetHeight(), img.GetWidth())

results in

array_view[0] = 123
ValueError: assignment destination is read-only

If I use numpy.astype instead with copy=False (i.e. inplace)

def get_array_from_buffer(img):
	image_memory_view = sitk._SimpleITK._GetMemoryViewFromImage(img)
	array_view = np.asarray(image_memory_view).astype(dtype=np.float32, copy=False)
	array_view[0] = 123
	
	# reshape: sitk (x:columns, y:rows, z:slices) -> numpy (z:slices, y:rows, x:columns)
	return array_view.reshape(img.GetDepth(), img.GetHeight(), img.GetWidth())

I can actually modify the numpy array without complaint but now the size and the values of the array (i.e. the memory interpretation) is off and I cannot reshape.

return array_view.reshape(img.GetDepth(), img.GetHeight(), img.GetWidth())
ValueError: cannot reshape array of size 1048576 into shape (1,512,512)

The array_view.shape (as well as image_memory_view.shape) is (1048576,) and therefore 4 x 512*512 which is the pixel matrix. Any suggestions on how to juggle this around to make it work?

from simpleitk.

greg814 avatar greg814 commented on June 2, 2024

Figured it out using memory view as suggested. I couldn't obtain a writable numpy array directly from sitk no matter what I tried, but numpy offers access to the buffer of any array, so I could trick it into being writeable:

import numpy as np
import SimpleITK as sitk
from SimpleITK.SimpleITK import _GetMemoryViewFromImage

def get_array_from_buffer(img: sitk.Image):
	buffer_type = ctypes.c_float * img.GetNumberOfPixels()

	# SimpleITK version: < 2.3.0
	# buffer      = buffer_type.from_address(int(img.GetBufferAsFloat()))
	# SimpleITK version: all
	memory_view	= sitk._SimpleITK._GetMemoryViewFromImage(img)
	array_view	= np.asarray(memory_view).view(dtype=np.float32)
	buffer      = buffer_type.from_address(array_view.ctypes.data)

	array_writeable = np.frombuffer(buffer, dtype=np.float32)

	# reshape: sitk (x:columns, y:rows, z:slices) -> numpy (z:slices, y:rows, x:columns)
	return array_writeable.reshape(img.GetSize()[::-1])

# Test

# create zero image (3 rows x 3 columns x 2 slices) of floats
img = sitk.Image((3,3,2), sitk.sitkFloat32)

arr = get_array_from_buffer(img)
print('arr =', arr)
print('arr[0,2,1] =', arr[0,2,1], '   img[1,2,0] =', img[1,2,0], '\n')

# replace pixel value and check if it happened in the buffer
print('arr[0,2,1] = 4321\n')
arr[0,2,1] = 4321
print('arr =', arr)
print('arr[0,2,1] =', arr[0,2,1], '   img[1,2,0] =', img[1,2,0], '\n')

print('arr += 10\n')
arr += 10
print('arr =', arr)

I have a couple of requests before you close this issue:

  1. Please document here (and wherever else appropriate) if buffer access was removed on purpose and why, just in case someone else bumps into this during their search. I couldn't find anything in my search before opening the issue.
  2. Reconsider removing buffer access. If numpy allows it, then you can. Also, for someone coming from C++ ITK it feels weird to work with the buffer in one language but being denied access in another.

FYI, Google Bard hallucinated to me that there is a sitk.Image.GetPixelPointer() function in version 2.3.2 until I made it aware of the fact that there is no such version released. Maybe Alphabet is watching your activities and my issue here is just some brief episode and a fix is coming soon with such a new function. Would still be good to document somewhere.

Thanks!

from simpleitk.

blowekamp avatar blowekamp commented on June 2, 2024

The GetBuffer methods were inadvertently added in 6c2ea55 and released in v2.1.x. Then the behavior was removed 66005b0, for release 2.3.x. So SimpleITK release 2.1.x, and 2.2.x appear to mistakenly have these GetBuffer methods.

Python 3.12 added support for PEP688 which enables proper buffer protocols for native python classes. This looks like a good way to improve our interface moving forward. Contributions are welcomed.

from simpleitk.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.