Skip to content
Snippets Groups Projects
df3.py 11.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • ################################################################################
    #
    # df3.py
    #
    # Copyright (C) 2005 Mike Kost <contact@povray.tashcorp.net>
    #
    # This library is free software; you can redistribute it and/or
    # modify it under the terms of the GNU Lesser General Public
    # License as published by the Free Software Foundation; either
    # version 2.1 of the License, or (at your option) any later version.
    #
    # This library is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    # Lesser General Public License for more details.
    #
    # You should have received a copy of the GNU Lesser General Public
    # License along with this library; if not, write to the Free Software
    # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    #
    #
    ################################################################################
    #
    # 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
    import sys
    
    # -+-+-+- Start df3 Class -+-+-+-
    
    class df3:
        __version__ = '0.2'
    
        __arraytype__ = 'f'
    
        __struct4byte__  = '>I'
        __struct2byte__  = '>H'
        __struct2byte3__ = '>HHH'
        __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__)
            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 = []
            tmp.append(self.sizeX())
            tmp.append(self.sizeY())
            tmp.append(self.sizeZ())
            return tmp
    
        #### Voxel Access Functions
    
        def get(self, x, y, z):
            return self.voxel[self.__voxa__(x,y,z)]
    
        def getB(self, x, y, z):
            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
    
            return self.voxel[self.__voxa__(x,y,z)]
    
        def set(self, x, y, z, val):
            self.voxel[self.__voxa__(x,y,z)] = val
    
        def setB(self, x, y, z, val):
            if (x > self.sizeX() or x < 0): return
            if (y > self.sizeX() or y < 0): return
            if (z > self.sizeX() or z < 0): return
    
            self.voxel[self.__voxa__(x,y,z)] = val
    
        #### Scalar Functions
    
        def mult(self, val):
            for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
                self.voxel[i] = self.voxel[i] * val
    
            return self
    
        def add(self, val):
            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]
    
            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]
    
            for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
                if (self.voxel[i] < tmp):
                    tmp = self.voxel[i]
    
            return tmp
    
        #### Vector Functions
    
        def compare(self, indf3):
            if (self.__samesize__(indf3) == 0): return 0
    
            if (self.voxel == indf3.voxel):
                return 1
    
            return 0
    
        def multV(self, indf3):
            if (self.__samesize__(indf3) == 0):
                print("Cannot multiply voxels - not same size")
                return
    
            for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
                self.voxel[i] = self.voxel[i]*indf3.voxel[i]
    
            return self
    
        def addV(self, indf3):
            if (self.__samesize__(indf3) == 0):
                print("Cannot add voxels - not same size")
                return
    
            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()
            if (fx % 2 != 1):
                print("Incompatible filter - must be odd number of X")
                return self
            if (fy % 2 != 1):
                print("Incompatible filter - must be odd number of Y")
                return self
            if (fz % 2 != 1):
                print("Incompatible filter - must be odd number of Z")
                return self
    
            fdx = (fx-1)/2
            fdy = (fy-1)/2
            fdz = (fz-1)/2
            flen = fx*fy*fz
    
            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()):
                        rip = self.__rip__(x-fdx, x+fdx, y-fdy, y+fdy, z-fdz, z+fdz)
                        tmp = 0.0
                        for i in range(flen):
                            tmp += rip[i]*filt.voxel[i]
                        newV[self.__voxa__(x,y,z)] = tmp
    
            self.voxel = newV
    
            return self
    
        #### Import/Export Functions
    
        def exportDF3(self, file, depth=8, rescale=1):
            x = self.sizeX()
            y = self.sizeY()
            z = self.sizeZ()
    
            try:
                f = open(file, 'wb');
            except:
                print("Could not open " + file + " for write");
                return
    
            f.write(struct.pack(self.__struct2byte3__, x, y, z));
    
            tmp = self.__toInteger__(pow(2,depth)-1, rescale)
    
            if (depth > 16): # 32-bit
                for i in range( x*y*z ):
                    f.write(struct.pack(self.__struct4byte__, tmp[i]))
            elif (depth > 8): # 16-bit
                for i in range( x*y*z ):
                    f.write(struct.pack(self.__struct2byte__, tmp[i]))
            else:
                for i in range( x*y*z ):
                    f.write(struct.pack(self.__struct1byte__, tmp[i]))
    
        def importDF3(self, file, scale=1):
            try:
                f = open(file, 'rb');
                size = os.stat(file)[stat.ST_SIZE]
    
            except:
                print("Could not open " + file + " for read");
                return []
    
            (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
    
            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
    
            if (format == 32):
                for i in range(x*y*z):
                    self.voxel[i] = float(struct.unpack(self.__struct4byte__, f.read(4) )[0])
            elif (format == 16):
                for i in range(x*y*z):
                    self.voxel[i] = float(struct.unpack(self.__struct2byte__, f.read(2) )[0])
            elif (format == 8):
                for i in range(x*y*z):
                    self.voxel[i] = float(struct.unpack(self.__struct1byte__, f.read(1) )[0])
    
            return self
    
        #### Local classes not intended for user use
    
        def __rip__(self, minX, maxX, minY, maxY, minZ, maxZ):
            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
                        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
                        # Check Y
                        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
                        # Check Z
                        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
                        else:
                            tmpV[(z*sizeZ+y)*sizeY+x] = self.get(minX+x,minY+y,minZ+z)
    
            return tmpV
    
        def __samesize__(self, indf3):
            if (self.sizeX() != indf3.sizeX()): return 0
            if (self.sizeY() != indf3.sizeY()): return 0
            if (self.sizeZ() != indf3.sizeZ()): return 0
            return 1
    
        def __voxa__(self, x, y, z):
            return ((z*self.sizeY()+y)*self.sizeX()+x)
    
        def __create__(self, x, y, z, atype='0', init=1):
            if (atype == '0'):
                tmp = self.__arraytype__
            else:
                tmp = atype
    
            if (init == 1):
                if tmp in ('f','d'):
                    voxel = array.array(tmp, [0.0 for i in range(x*y*z)])
                else:
                    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):
            if (scale < pow(2,8)): # 8-bit
                tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array1byte__)
            elif (scale < pow(2,16)): # 16-bit
                tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array2byte__)
            else: # 32-bit
                tmp = self.__create__(self.sizeX(), self.sizeY(), self.sizeZ(), self.__array4byte__)
    
            maxVal = self.max()
    
            print(scale)
    
            for i in range(self.sizeX()*self.sizeY()*self.sizeZ()):
                if (rescale == 1):
                    tmp[i] = max(0,int(round(scale*self.voxel[i]/maxVal)))
                else:
                    tmp[i] = max(0,min(scale,int(round(self.voxel[i]))))
    
            return tmp
    
    # -=-=-=- End df3 Class -=-=-=-
    
    ##########DEFAULT EXAMPLES
    # if __name__ == '__main__':
        # localX = 80
        # localY = 90
        # localZ = 100
        ## Generate an output
        # 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)
    
        # temp.exportDF3('temp.df3', 16)
    ###############################################################################
        ## Import
        # temp2 = df3().importDF3('temp.df3')
        # temp2.mult(1/temp2.max())
    
        ## Compare
        # print(temp2.size())
    
        # 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
    ###############################################################################