Skip to content
Snippets Groups Projects
df3_library.py 10.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    # Copyright 2005 Mike Kost <contact@povray.tashcorp.net>
    
    
    ################################################################################
    #
    # df3.py
    #
    
    # -----------------------------------------------------------------------------
    
    #
    # Creation functions
    #     __init__(x=1, y=1, z=1) : default constructor
    #     clone(indf3) : make this df3 look like indf3
    #
    # Info functions
    
    #     sizeX(): returns X dimension
    #     sizeY(): returns Y dimension
    #     sizeZ(): returns Z dimension
    
    #
    # Scalar functions
    #     mult():
    #     add():
    #     max(): returns highest voxel value in df3
    #     min(): returns lowest voxel value in df3
    #
    # Vector functions
    #
    # Import/Export functions
    #     exportDF3():
    #     importDF3():
    #
    
    # -----------------------------------------------------------------------------
    
    
    import struct
    import os
    import stat
    import array
    
    # -+-+-+- Start df3 Class -+-+-+-
    
    
    class df3:
        __version__ = '0.2'
    
        __arraytype__ = 'f'
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        __struct4byte__ = '>I'
        __struct2byte__ = '>H'
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
        __struct1byte__ = '>B'
        __array4byte__ = 'I'
        __array2byte__ = 'H'
        __array1byte__ = 'B'
    
    
        def __init__(self, x=1, y=1, z=1):
            self.maxX = x
            self.maxY = y
            self.maxZ = z
            self.voxel = self.__create__(x, y, z)
    
        def clone(self, indf3):
            self.voxel = array.array(self.__arraytype__)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(indf3.sizeX() * indf3.sizeY() * indf3.sizeZ()):
    
                self.voxel[i] = indf3.voxel[i]
            return self
    
    
        # --- Info Functions
    
    
        def sizeX(self):
            return self.maxX
    
        def sizeY(self):
            return self.maxY
    
        def sizeZ(self):
            return self.maxZ
    
        def size(self):
    
            tmp = [self.sizeX()]
    
            tmp.append(self.sizeY())
            tmp.append(self.sizeZ())
            return tmp
    
    
        # --- Voxel Access Functions
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            return self.voxel[self.__voxa__(x, y, z)]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if x > self.sizeX() or x < 0:
                return 0
            if y > self.sizeX() or y < 0:
                return 0
            if z > self.sizeX() or z < 0:
                return 0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            return self.voxel[self.__voxa__(x, y, z)]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            self.voxel[self.__voxa__(x, y, z)] = val
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if x > self.sizeX() or x < 0:
                return
            if y > self.sizeX() or y < 0:
                return
            if z > self.sizeX() or z < 0:
                return
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            self.voxel[self.__voxa__(x, y, z)] = val
    
        # --- Scalar Functions
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
    
                self.voxel[i] = self.voxel[i] * val
    
            return self
    
        def add(self, val):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
    
                self.voxel[i] = self.voxel[i] + val
    
            return self
    
        def max(self):
            tmp = self.voxel[0]
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
                if self.voxel[i] > tmp:
    
                    tmp = self.voxel[i]
    
            return tmp
    
        def min(self):
            tmp = self.voxel[0]
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
                if self.voxel[i] < tmp:
    
        # --- Vector Functions
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if self.__samesize__(indf3) == 0:
                return 0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if self.voxel == indf3.voxel:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if self.__samesize__(indf3) == 0:
    
                print("Cannot multiply voxels - not same size")
                return
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
                self.voxel[i] = self.voxel[i] * indf3.voxel[i]
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if self.__samesize__(indf3) == 0:
    
                print("Cannot add voxels - not same size")
                return
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
                self.voxel[i] = self.voxel[i] + indf3.voxel[i]
    
    
            return self
    
        def convolveV(self, filt):
            fx = filt.sizeX()
            fy = filt.sizeY()
            fz = filt.sizeZ()
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if fx % 2 != 1:
    
                print("Incompatible filter - must be odd number of X")
                return self
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if fy % 2 != 1:
    
                print("Incompatible filter - must be odd number of Y")
                return self
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if fz % 2 != 1:
    
                print("Incompatible filter - must be odd number of Z")
                return self
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            fdx = (fx - 1) / 2
            fdy = (fy - 1) / 2
            fdz = (fz - 1) / 2
            flen = fx * fy * fz
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            newV = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ())
    
    
            for x in range(self.sizeX()):
                for y in range(self.sizeY()):
                    for z in range(self.sizeZ()):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        rip = self.__rip__(x - fdx, x + fdx, y - fdy, y + fdy, z - fdz, z + fdz)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            tmp += rip[i] * filt.voxel[i]
                        newV[self.__voxa__(x, y, z)] = tmp
    
        # --- Import/Export Functions
    
    
        def exportDF3(self, file, depth=8, rescale=1):
            x = self.sizeX()
            y = self.sizeY()
            z = self.sizeZ()
    
            try:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                f = open(file, 'wb')
    
            except BaseException as e:
                print(e.__doc__)
                print('An exception occurred: {}'.format(e))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                print("Could not open " + file + " for write")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            f.write(struct.pack(self.__struct2byte3__, x, y, z))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            tmp = self.__toInteger__(pow(2, depth) - 1, rescale)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if depth > 16:  # 32-bit
                for i in range(x * y * z):
    
                    f.write(struct.pack(self.__struct4byte__, tmp[i]))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            elif depth > 8:  # 16-bit
                for i in range(x * y * z):
    
                    f.write(struct.pack(self.__struct2byte__, tmp[i]))
            else:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                for i in range(x * y * z):
    
                    f.write(struct.pack(self.__struct1byte__, tmp[i]))
    
        def importDF3(self, file, scale=1):
            try:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                f = open(file, 'rb')
    
            except BaseException as e:
                print(e.__doc__)
                print('An exception occurred: {}'.format(e))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                print("Could not open " + file + " for read")
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            (x, y, z) = struct.unpack(self.__struct2byte3__, f.read(6))
    
    
            self.voxel = self.__create__(x, y, z)
            self.maxX = x
            self.maxY = y
            self.maxZ = z
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            size = size - 6
            if size == x * y * z:
                format = 8
            elif size == 2 * x * y * z:
                format = 16
            elif size == 4 * x * y * z:
                format = 32
    
    
            for i in range(x * y * z):
                if format == 32:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    self.voxel[i] = float(struct.unpack(self.__struct4byte__, f.read(4))[0])
    
                elif format == 16:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    self.voxel[i] = float(struct.unpack(self.__struct2byte__, f.read(2))[0])
    
                elif format == 8:
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    self.voxel[i] = float(struct.unpack(self.__struct1byte__, f.read(1))[0])
    
        # --- Local classes not intended for user use
    
    
        def __rip__(self, minX, maxX, minY, maxY, minZ, maxZ):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            sizeX = maxX - minX + 1
            sizeY = maxY - minY + 1
            sizeZ = maxZ - minZ + 1
    
    
            tmpV = self.__create__(sizeX, sizeY, sizeZ)
    
            for x in range(sizeX):
                for y in range(sizeY):
                    for z in range(sizeZ):
                        # Check X
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        if (minX + x) < 0:
                            tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
                        elif (minX + x) > self.sizeX() - 1:
                            tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        elif (minY + y) < 0:
                            tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
                        elif (minY + y) > self.sizeY() - 1:
                            tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                        elif (minZ + z) < 0:
                            tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
                        elif (minZ + z) > self.sizeZ() - 1:
                            tmpV[(z * sizeZ + y) * sizeY + x] = 0.0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                            tmpV[(z * sizeZ + y) * sizeY + x] = self.get(minX + x, minY + y, minZ + z)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if self.sizeX() != indf3.sizeX():
                return 0
            if self.sizeY() != indf3.sizeY():
                return 0
            if self.sizeZ() != indf3.sizeZ():
                return 0
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            return (z * self.sizeY() + y) * self.sizeX() + x
    
    
        def __create__(self, x, y, z, atype='0', init=1):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if atype == '0':
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if init == 1:
                if tmp in ('f', 'd'):
                    voxel = array.array(tmp, [0.0 for i in range(x * y * z)])
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    voxel = array.array(tmp, [0 for i in range(x * y * z)])
    
            else:
                voxel = array.array(tmp)
    
            return voxel
    
        def __toInteger__(self, scale, rescale=1):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            if scale < pow(2, 8):  # 8-bit
    
                tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array1byte__)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            elif scale < pow(2, 16):  # 16-bit
    
                tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array2byte__)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            else:  # 32-bit
    
                tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array4byte__)
    
            maxVal = self.max()
    
            print(scale)
    
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            for i in range(self.sizeX() * self.sizeY() * self.sizeZ()):
                if rescale == 1:
                    tmp[i] = max(0, int(round(scale * self.voxel[i] / maxVal)))
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                    tmp[i] = max(0, min(scale, int(round(self.voxel[i]))))
    
    # --------DEFAULT EXAMPLES
    
    # if __name__ == '__main__':
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # localX = 80
    # localY = 90
    # localZ = 100
    
    # -- Generate an output
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # temp = df3(localX, localY, localZ)
    
    # for i in range(localX):
    
    #   for j in range(localY):
    #       for k in range(localZ):
    #           if (i >= (localX/2)):
    #               temp.set(i, j, k, 1.0)
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    
    # temp.exportDF3('temp.df3', 16)
    
    # -----------------------------------------------------------------------------
    # -- Import
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # temp2 = df3().importDF3('temp.df3')
    # temp2.mult(1/temp2.max())
    
    # -- Compare
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # print(temp2.size())
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
    # if (temp.compare(temp2) == 0): print("DF3's Do Not Match")
    
    # -----------------------------------------------------------------------------
    
    # ChangeLog
    # ---------
    # 08/09/05: 0.20 released
    #    + Change internal representation to floating point
    #    + Rewrite import/export for speed
    #    + Convert from 3-d list structure to Array class for data storage
    #    + Add element access, scalar, and vector functions
    # 07/13/05: 0.10 released
    
    # -----------------------------------------------------------------------------