Help: Get floating point value from tiff, but i get values of 255

Hi guys, I am reading in a geo tif, but when I use System.Drawing.Bitmap.GetPixel(bitmap,wi,hi) I get the pixels, but the values I get are all the same:
Color [A=255, R=255, G=255, B=255]

But why is System limiting me to the 0-255 step RGB values? I need the floating point values, can I get those?

If I open the image in photoshop it appears all white, but stacking a few brightness and contrast filters reveal that the image has fine neuances:

Tif is here: (Had to Zip it because discource doesn’t support tif)
DTM_32-1-514-134-76.zip (1.7 MB)

1 Like

@nathanletwory do you know anything about this?

Here is a super-simple print a pixel script:

import rhinoscriptsyntax as rs
import System

path=rs.OpenFileName("Selct image to trace", filter="TIF (*.tif)|*.tif| TIFF (*.tiff)|*.tiff| |")
bitmap = System.Drawing.Bitmap.FromFile(path,False)
print bitmap.PixelFormat
x=100
y=100
print bitmap.GetPixel(x,y)

The result:
Format32bppArgb
Color [A=255, R=255, G=255, B=255]

(And all pixles return the same value)


on a mac:
affinity photo assigns a D50 profile on opening and your image looks like this…
preview will open the tiff all white.
Do you have any further info about the tiff file ? colordepth, created by…
Do you have the same problem if you read in a tiff with a gradient from white to black ?

Thanks for testing Tom, I don’t have any more info other than that it is in Format32bppArgb so I am confused why the values are returned as 0-255 values instead of 0.0-1.0
It seems awfully limiting to return the pixel as truncated… So I must do something wrong imo.

check out this

" The problem is that C# (or better said the underlying API) can’t handle Greyscale images with a Colordepth greater than 8bit.

I’d suggest using LibTiff.NET for Handling TIFF images. When i faced such an problem, i loaded the TIFF image raw Data into an array"

looks like bitmap → getpixel returns a color and the A R G B values of color is byte (8 bit , 0…255) only.

you need to use another libary or class to read and evaluate this kind of bitmap.
or convert it in photoshop first

1 Like

Ok, that is so strange since SetPixel easily handles floating numbers.

Thanks for your time Tom! I was really wanting to not having to rely on libraries that has to be installed/located :slight_smile:

do you need to do it once ? is it your everyday workflow ? or is it a plug-in-development ?
if you just need to do the import once (and don t care about the complexity / performance of the workflow), you might find a geo-tiff to .csv converter and read in the text from the .csv.

sorry if i can t help further, but it looks like some special knowledge is needed about those geo-tiffs and reading them

but maybe some ot “the bigger brains” …

kind regards - tom

The System.Drawing.Bitmap GetPixel/SetPixel are very limited indeed. Let me see how you could do this with the RDK.

2 Likes

A, great, thanks!

1 Like

I’ll have to type up a proper example later today (currently preparing lunch), but the approach would be:

<<read TIFF Bitmap>>
<<create render textuer using RenderTexture.NewBitmapTexture>>
<<get evaluator from texture>>
<<double loop over evaluator to get values>>

I am assuming here now that the render texture will maintain the bit depth of each TIFF channel.

Functions to be used:

Here how RhinoCycles uses TextureEvaluator to access RenderTexture colors

1 Like

@Holo I’m working on an example script, but I’m realizing that we have a problem with Rhino and 32bit TIFFs. Your TIFF has values way higher than 1.0f, but for instance Rhino itself loads the image like this:

kuva

at least on Rhino for Mac.

I’ll first have to figure out if Rhino currently properly supports TIFF as HDRi otherwise we can’t use RenderTexture and TextureEvaluator.

Logged as RH-67413 Allow usage of 32bit TIFF

Blender shows:

1 Like

Thanks for looking into this. Yeah, the values are high, but they work fine in Photoshop so I am sure you can get to them one way or another :slight_smile:

Thanks for looking into it Nathan!

Yes, probably “just” a matter of ensuring we can create High Dynamic Range textures in Rhino with a TIFF, that way we can access values over 1.0 and belown 0.0. For instance EXR and HDR files can be loaded and evaluated with that. These type of textures work fine with Rhino Cycles using the evaluator.

1 Like

What the… what does it mean to evaluate a “bitmap” with a 3dpoint… and with vectors? That passed sooo high above my head… :smiley:

I get results, so stuff works, but I get the same value for all pixels, so obviously I am doing something wrong.

import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
import System

### Locate file
path=rs.OpenFileName("Selct image to trace", filter="TIF (*.tif)|*.tif| TIFF (*.tiff)|*.tiff| |")
#path=rs.OpenFileName("Selct image to trace", filter="PNG (*.png)|*.png| |")

### Load file and extract width and height
bitmap = System.Drawing.Bitmap.FromFile(path,False)
width = bitmap.Width
height = bitmap.Height

### print data
print bitmap.PixelFormat
print width,height

### Setup a RenderTexture and an Evaluator
RenderTexture = Rhino.Render.RenderTexture.NewBitmapTexture(bitmap,sc.doc.ActiveDoc)
Evaluator = RenderTexture.CreateEvaluator()

### I am adding vectors for the evaluator... have NO idea what they do so I point them in different directions just in case
v1 = rs.coerce3dvector((1,0,0))
v2 = rs.coerce3dvector((0,1,0))

### Make a list for points for the pointCloud
points = []

### Evaluate every pixel and add point to list
for x in range(width):
    for y in range(height):
        pt = rs.coerce3dpoint((x,y,0))
        color = Evaluator.GetColor(pt,v1,v2)
        px = (color.A+color.R+color.G+color.B)*1000
        points.append(rs.coerce3dpoint((x,y,px)))

### Add pointcloud and select it
cloud = rs.AddPointCloud(points)
rs.SelectObject(cloud)

You are not doing anything wrong. Our Bitmap Texture does not understand high dynamic range textures.

And unfortunately at the moment our High Dynamic Range Texture accepts only EXR and HDR, not TIFF. So at the moment we can’t use TIFF images.

Of course, if you can save it as an EXR or HDR we should be able to do something with it.

Using Blender to resave the TIFF as EXR, then bring it manually into Rhino as High Dynamic Range Texture gives

If you are OK with the extra step for now then this is something to get you forward.

… or just do it the right way by getting that into QGIS and exporting a mesh from that…

… or just using the Bison Grasshopper plug-in to read the GeoTIFF and get the mesh right away.
-wim

That is probably not what @Holo is looking for for his own TerrainMesh plug-in.

1 Like

There might be a “right way”… but that is not the fun way…
I am exploring the ability to skip some steps, and the process is really quite simple once the data can be read. (If/when it can be read :wink: )

I saved is as an EXR but I get an “Error: Out of memory”

path=rs.OpenFileName(“Selct image to trace”, filter=“EXR (.exr)|.exr| |”)
bitmap = System.Drawing.Bitmap.FromFile(path,False)

And if I load a JPEG then I also get the same values for every pixel.

import Rhino
import scriptcontext as sc
import rhinoscriptsyntax as rs
import System

### Locate file
#path=rs.OpenFileName("Selct image to trace", filter="TIF (*.tif)|*.tif| TIFF (*.tiff)|*.tiff| |")
#path=rs.OpenFileName("Selct image to trace", filter="EXR (*.exr)|*.exr| |")
#path=rs.OpenFileName("Selct image to trace", filter="PNG (*.png)|*.png| |")
path=rs.OpenFileName("Selct image to trace", filter="JPG (*.jpg)|*.jpg| JPEG (*.jpeg)|*.jpeg| |")

### Load file and extract width and height
bitmap = System.Drawing.Bitmap.FromFile(path,False)
width = bitmap.Width
height = bitmap.Height

### print data
print bitmap.PixelFormat
print width,height

### Setup a RenderTexture and an Evaluator
RenderTexture = Rhino.Render.RenderTexture.NewBitmapTexture(bitmap,sc.doc.ActiveDoc)
Evaluator = RenderTexture.CreateEvaluator()

### I am adding vectors for the evaluator... have NO idea what they do so I point them in different directions just in case
v1 = rs.coerce3dvector((0,0,0))
v2 = rs.coerce3dvector((0,0,0))

### Make a list for points for the pointCloud
points = []

### Evaluate every pixel and add point to list
for x in range(width):
    for y in range(height):
        pt = rs.coerce3dpoint((x,y,0))
        color = Evaluator.GetColor(pt,v1,v2)
        if x== 100 and y == 100: 
            print color.A,color.R,color.G,color.B
        if x== 200 and y == 200: 
            print color.A,color.R,color.G,color.B
        px = (color.A+color.R+color.G+color.B)
        points.append(rs.coerce3dpoint((x,y,px)))

### Add pointcloud and select it
cloud = rs.AddPointCloud(points)
rs.SelectObject(cloud)

jpeg:


exr:
DTM_32-1-514-134-76 (2).zip (339.8 KB)

1 Like