ROS CompressedDepth to numpy (or cv2) - python

Folks,
I am using this link as starting point to convert my CompressedDepth (image of type: "32FC1; compressedDepth," in meters) image to OpenCV frames:
Python CompressedImage Subscriber Publisher
I get an empty data when I try to print, or I get a NonType when I see the result of my array, etc.
What is the right way to convert a compressedDepth image?
Republishing is not gonna work do to wifi/router bandwidth and speed constraints.

The right way to decode compressedDepth is to first remove the header from the raw data and then convert the remaining data.
This is documented in image_transport_plugins/compressed_depth_image_transport/src/codec.cpp.
On my machine the header size is 12 bytes. This might however be different on other architectures since the size of an enum is not defined.
The following python code snippet exports compressed 16UC1 and 32FC1 depth images as png file:
# 'msg' as type CompressedImage
depth_fmt, compr_type = msg.format.split(';')
# remove white space
depth_fmt = depth_fmt.strip()
compr_type = compr_type.strip()
if compr_type != "compressedDepth":
raise Exception("Compression type is not 'compressedDepth'."
"You probably subscribed to the wrong topic.")
# remove header from raw data
depth_header_size = 12
raw_data = msg.data[depth_header_size:]
depth_img_raw = cv2.imdecode(np.fromstring(raw_data, np.uint8), cv2.CV_LOAD_IMAGE_UNCHANGED)
if depth_img_raw is None:
# probably wrong header size
raise Exception("Could not decode compressed depth image."
"You may need to change 'depth_header_size'!")
if depth_fmt == "16UC1":
# write raw image data
cv2.imwrite(os.path.join(path_depth, "depth_" + str(msg.header.stamp) + ".png"), depth_img_raw)
elif depth_fmt == "32FC1":
raw_header = msg.data[:depth_header_size]
# header: int, float, float
[compfmt, depthQuantA, depthQuantB] = struct.unpack('iff', raw_header)
depth_img_scaled = depthQuantA / (depth_img_raw.astype(np.float32)-depthQuantB)
# filter max values
depth_img_scaled[depth_img_raw==0] = 0
# depth_img_scaled provides distance in meters as f32
# for storing it as png, we need to convert it to 16UC1 again (depth in mm)
depth_img_mm = (depth_img_scaled*1000).astype(np.uint16)
cv2.imwrite(os.path.join(path_depth, "depth_" + str(msg.header.stamp) + ".png"), depth_img_mm)
else:
raise Exception("Decoding of '" + depth_fmt + "' is not implemented!")

Related

How to specify burn values by attribute using GDAL rasterize (python API)?

I'm using gdal.RasterizeLayer() to convert a shapefile to a GeoTiff using a GeoTiff template while burning the output values by ATTRIBUTE. What I want to output is a .tif where the burn value corresponds to the value of a given attribute. What I find is that gdal.RasterizeLayer() is burning to strange values that do not correspond to the values in my attribute field. Here's what I have currently:
gdalformat = 'GTiff'
datatype = gdal.GDT_Byte
# Open Shapefile
shapefile = ogr.Open(self.filename)
shapefile_layer = shapefile.GetLayer()
# Get projection info from reference image
image = gdal.Open(ref_image, gdal.GA_ReadOnly)
output = gdal.GetDriverByName(gdalformat).Create(output_tif, image.RasterXSize, image.RasterYSize, 1, datatype,
options=['COMPRESS=DEFLATE'])
output.SetProjection(image.GetProjectionRef())
output.SetGeoTransform(image.GetGeoTransform())
# Write data to band 1
band = output.GetRasterBand(1)
band.SetNoDataValue(0)
gdal.RasterizeLayer(output, [1], shapefile_layer, options=['ATTRIBUTE=FCode'])
# Close datasets
band = None
output = None
image = None
shapefile = None
# Build image overviews
subprocess.call("gdaladdo --config COMPRESS_OVERVIEW DEFLATE " + output_tif + " 2 4 8 16 32 64", shell=True)
What occurs is that the output .tif correctly assigns different burn values for each attribute, but the value does not correspond to the attribute value. For example, the input attribute value FCode=46006 turns into a burn value of 182 (and it's not clear why!). I tried adding and removing the 'COMPRESS=DEFLATE' option, and adding and removing the '3D' option for gdal.RasterizeLayer(). None affect the output burn values.
You can see the input shapefile and attribute values here: input .shp
And the output, with the incorrect values, here: output raster
I fixed this myself by changing the type to gdal.GDT_Int32.

Unable to find headers of jpg while reading a raw disk image (dd)

I am reading a raw disk image using python 3. My task is to retrieve (carve) jpgs as individual files from the disk image. As I know header pattern (\xd8\xff\xe0 or \xd8\xff\xe1) of jpg. I want to know where I get this while reading file.
fobj = open('carve.dd', 'rb')
data = fobj.read(32)
while data != '':
head_loc = findheader(data)
print(head_loc)
data = fobj.read(32)
def findheader(data) : # to find header in each 32 bytes of data of raw disk image
for i in range(0, len(data) - 3) :
if data[i] == b'\xff' :
if data[i+1:i+4] == b'\xd8\xff\xe0' or data[i+1:i+4] == b'\xd8\xff\xe1' :
return i
return -1
The same code is working fine in Python 2. In Python 2, I am able to get headers in just a few seconds from image. Can someone help me out, what is the problem in Python 3?
This code snippet is actually from this https://github.com/darth-cheney/JPEG-Recover/blob/master/jpegrecover2.py
This runs fine in Python 2 but not in Python 3. Please forget about inconsistent tab error when you run the code in link. I again retyped in VS code.
Like the old saying goes, I've got some bad news and some good news. The bad is I can't figure out why your code doesn't work the same in both version 2 and version 3 of Python.
The good is that I was able to reproduce the problem using the sample data you provided, but—more importantly—able to devise something that not only works consistently in both versions, it's likely much faster because it doesn't use a for loop to search through each chunk of data looking for the .jpg header patterns.
from __future__ import print_function
LIMIT = 100000 # Number of chunks (for testing).
CHUNKSIZE = 32 # Bytes.
HDRS = b'\xff\xd8\xff\xe0', b'\xff\xd8\xff\xe1'
IMG_PATH = r'C:\vols\Files\Temp\carve.dd.002'
print('Searching...')
with open(IMG_PATH, 'rb') as file:
chunk_index = 0
found = 0
while True:
data = file.read(CHUNKSIZE)
if not data:
break
# Search for each of the headers in each chunk.
for hdr in HDRS:
offset = 0
while offset < (CHUNKSIZE - len(hdr)):
try:
head_loc = data[offset:].index(hdr)
except ValueError: # Not found.
break
found += 1
file_offset = chunk_index*CHUNKSIZE + head_loc
print('found: #{} at {:,}'.format(found, file_offset))
offset += (head_loc + len(hdr))
chunk_index += 1
if LIMIT and (chunk_index == LIMIT): break # Stop after this many chunks.
print('total found {}'.format(found))

Creating image files that's named in numerical sequence

I have a script that's supposed to open a png image and then resize it and then save it as an jpg in numerical sequence. But the code for the number sequencing I copied from the internet isn't working with PIL. It gives me the exception "KeyError: 'W'"
import os
from PIL import Image
os.chdir('C:\\Users\\paul\\Downloads')
# open canvas.png
original = Image.open('canvas.png')
# resize image height to 2160
size = (3000, 2160)
original.thumbnail(size)
# convert to RGB
RGB = original.convert('RGB')
# save image as sequence
i = 0
while os.path.exists("image%s.jpg" % i):
i += 1
RGB.save("image%s.jpg" % i, "w")
Is there another way to do this?
Edit based on Haken Lid's comment
The PIL documentation says that the function save accepts these argument:
Image.save(fp, format=None, **params)
The parameter w you passed is not within the set of accepted file format.
Here you can see which formats are accepted. To make it works, just drop the w argument and substitute the %s with %d (i is an integer, not a string):
RGB.save("image%d.jpg" % i)
Note: from your tags it is not clear if you're using python2 or python3. If you are using python 3, I suggest to use the new method to format string:
RGB.save("image{}.jpg".format(i))
You can even specify a padding so that you can sort your file by name later on:
RGB.save("image{:04d}.jpg".format(i))
where 4 means that your number will be padded with zeros as to have length of at least 4.

Instructables open source code: Python IndexError: list index out of range

I've seen this error on several other questions but couldn't find the answer.
{I'm a complete stranger to Python, but I'm following the instructions from a site and I keep getting this error once I try to run the script:
IndexError: list index out of range
Here's the script:
##//txt to stl conversion - 3d printable record
##//by Amanda Ghassaei
##//Dec 2012
##//http://www.instructables.com/id/3D-Printed-Record/
##
##/*
## * This program is free software; you can redistribute it and/or modify
## * it under the terms of the GNU General Public License as published by
## * the Free Software Foundation; either version 3 of the License, or
## * (at your option) any later version.
##*/
import wave
import math
import struct
bitDepth = 8#target bitDepth
frate = 44100#target frame rate
fileName = "bill.wav"#file to be imported (change this)
#read file and get data
w = wave.open(fileName, 'r')
numframes = w.getnframes()
frame = w.readframes(numframes)#w.getnframes()
frameInt = map(ord, list(frame))#turn into array
#separate left and right channels and merge bytes
frameOneChannel = [0]*numframes#initialize list of one channel of wave
for i in range(numframes):
frameOneChannel[i] = frameInt[4*i+1]*2**8+frameInt[4*i]#separate channels and store one channel in new list
if frameOneChannel[i] > 2**15:
frameOneChannel[i] = (frameOneChannel[i]-2**16)
elif frameOneChannel[i] == 2**15:
frameOneChannel[i] = 0
else:
frameOneChannel[i] = frameOneChannel[i]
#convert to string
audioStr = ''
for i in range(numframes):
audioStr += str(frameOneChannel[i])
audioStr += ","#separate elements with comma
fileName = fileName[:-3]#remove .wav extension
text_file = open(fileName+"txt", "w")
text_file.write("%s"%audioStr)
text_file.close()
Thanks a lot,
Leart
Leart - check these it may help:
Is your input file in correct format? As I see it, you need to produce that file before hand before you can use it in this program... Post that file in here as well.
Check if your bitrate and frame rates are correct
Just for debugging purposes (if the code is correct, this may not produce correct results, but good for testing). You are accessing frameInt[4*i+1], with index i multiplied by 4 then adding 1 (going beyond the frameInt index eventually).
Add an 'if' to check size before accessing the array element in frameInt:
if len(frameInt)>=(4*i+1):
Add that statement right after the first occurence of "for i in range(numframes):" and just before "frameOneChannel[i] = frameInt[4*i+1]*2**8+frameInt[4*i]#separate channels and store one channel in new list"
*watch tab spaces

Alternative method to compare original image with edited one that may not need the original

A while back I did a python script to store data in images, however it has a small problem and I'm just wondering if anyone might be able to think of an alternative method.
A very basic idea is it'll pickle something, then with the first version, it directly wrote the ASCII numbers as pixels (since everything is between 0 and 255). That'll result in an image that looks a little like TV noise.
When writing into an actual image, it'll detect the minimum bits per pixel it needs to adjust so it'll not be noticeable to the human eye, and it'll split the data and add or subtract a few bits from each pixel, with the very first pixel storing the method it's using. I then store the URL as a file in the image, and can reverse it by comparing the original image from the URL with the current image using the rules given in the first pixel.
A bit of python pseudocode in case I didn't explain that well:
original_image = (200, 200, 200, 100, 210, 255...)
stuff_to_store = "test"
#Convert anything into a list of bytes
data_numbers = [bin(ord(x)) for x in cPickle.dumps(stuff_to_store)]
#This is calculated by the code, but for now it's 2
bytes_per_pixel = 2
store_mode = 'subtract'
#Join the bytes and split them every 2nd character
new_bytes = "".join(data_bytes)
new_bytes_split = [new_bytes[i:i+bytes_per_pixel] for i in range(0, len(new_bytes), bytes_per_pixel)]
#Edit the pixels (by subtraction in this case)
pixel_data = []
for i in range(len(original_image)):
pixel_data = original_image[i] - int(new_bytes_split[i])
However, since the whole point of the script is to store things by modifying the pixels, storing the original image URL as a file feels a bit of a cheaty way to do it. I thought of storing the URL as the first few pixels, but it'd end up with a noticeable line whenever the image wasn't grey. Also, this way is incredibly inefficient as it needs two images to work properly, so it'd be great if anyone had an idea of how to avoid doing this.
The original code is here if anyone is interested, I did it before I learned about writing the documentation so it's a bit hard to figure out, just asking this now as I'm planning on rewriting it and would like to do it better.
Here's one way to embed data into the least significant bit of each colour channel of the pixels in a 8 bit per channel RGB image file, using PIL to do the image handling.
The code below illustrates bit stream handling in Python. It's reasonably efficient (as far as such operations can be efficient in Python), but it sacrifices efficiency for readability & simplicity of use when necessary. :)
#! /usr/bin/env python
''' Steganography with PIL (really Pillow)
Encodes / decodes bits of a binary data file into the LSB of each color
value of each pixel of a non-palette-mapped image.
Written by PM 2Ring 2015.02.03
'''
import sys
import getopt
import struct
from PIL import Image
def readbits(bytes):
''' Generate single bits from bytearray '''
r = range(7, -1, -1)
for n in bytes:
for m in r:
yield (n>>m) & 1
def encode(image_bytes, mode, size, dname, oname):
print 'Encoding...'
with open(dname, 'rb') as dfile:
payload = bytearray(dfile.read())
#Prepend encoded data length to payload
datalen = len(payload)
print 'Data length:', datalen
#datalen = bytearray.fromhex(u'%06x' % datalen)
datalen = bytearray(struct.pack('>L', datalen)[1:])
payload = datalen + payload
databits = readbits(payload)
for i, b in enumerate(databits):
image_bytes[i] = (image_bytes[i] & 0xfe) | b
img = Image.frombytes(mode, size, str(image_bytes))
img.save(oname)
def bin8(i):
return bin(i)[2:].zfill(8)
bit_dict = dict((tuple(int(c) for c in bin8(i)), i) for i in xrange(256))
def decode_bytes(data):
return [bit_dict[t] for t in zip(*[iter(c&1 for c in data)] * 8)]
def decode(image_bytes, dname):
print 'Decoding...'
t = decode_bytes(image_bytes[:24])
datalen = (t[0] << 16) | (t[1] << 8) | t[2]
print 'Data length:', datalen
t = decode_bytes(image_bytes[24:24 + 8*datalen])
with open(dname, 'wb') as dfile:
dfile.write(str(bytearray(t)))
def process(iname, dname, oname):
with Image.open(iname) as img:
mode = img.mode
if mode == 'P':
raise ValueError, '%s is a palette-mapped image' % fname
size = img.size
image_bytes = bytearray(img.tobytes())
#del img
print 'Data capacity:', len(image_bytes) // 8 - 24
if oname:
encode(image_bytes, mode, size, dname, oname)
elif dname:
decode(image_bytes, dname)
def main():
#input image filename
iname = None
#data filename
dname = None
#output image filename
oname = None
def usage(msg=None):
s = msg + '\n\n' if msg else ''
s += '''Embed data into or extract data from the low-order bits of an image file.
Usage:
%s [-h] -i input_image [-d data_file] [-o output_image]
To encode, you must specify all 3 file names.
To decode, just specify the input image and the data file names.
If only the the input image is given, its capacity will be printed,
i.e., the maximum size (in bytes) of data that it can hold.
Uses PIL (Pillow) to read and write the image data.
Do NOT use lossy image formats for output, eg JPEG, or the data WILL get scrambled.
The program will abort if the input image is palette-mapped, as such images
are not suitable.
'''
print >>sys.stderr, s % sys.argv[0]
raise SystemExit, msg!=None
try:
opts, args = getopt.getopt(sys.argv[1:], "hi:d:o:")
except getopt.GetoptError, e:
usage(e.msg)
for o, a in opts:
if o == '-h': usage(None)
elif o == '-i': iname = a
elif o == '-d': dname = a
elif o == '-o': oname = a
if iname:
print 'Input image:', iname
else:
usage('No input image specified!')
if dname:
print 'Data file:', dname
if oname:
print 'Output image:', oname
process(iname, dname, oname)
if __name__ == '__main__':
main()

Categories

Resources