#! /usr/bin/env python
## -*- coding: utf-8 -*-
## (C) 2012: Hans Georg Schaathun <georg@schaathun.net>
"""
This module defines a scaling model, to scale features prior to
classification. It is used by the SVModel class, but is designed
with loose coupling to facilitate reuse with other classification
algorithms.
The ScaleModel class implements some of the interface of FeatureVector
and can be used in lieu thereof when getting feature values from
images.
The implementation is slow. Each feature value depends on three
tables and three records are queried separately from Feature,
FeatureValue, and Scaling. Combining the three in one view
to be queried in one operation is expected to be faster.
This module will auto-connect to the database and must be loaded
after options have been processed, to ensure correct connection.
The reason for this is that it depends on views defined server side.
"""
print "[pysteg.sql.scaling]"
from sqlobject import *
from sqlobject.views import *
from . import *
from .aux import isDuplicateError
import numpy as np
sqlConnect()
[docs]class ScaleModel(SQLObject):
"""This is a complete scaling model, with scaling formulæ for
each feature. It implements part of the interface of FeatureVector
and can be passed to the :meth:`getFeatures` methods of
Image, ImageSet, and TestSet to return complete scaled feature
vectors with canonical coordinate ordering."""
testset = ForeignKey( "TestSet" )
scales = SQLMultipleJoin( "Scaling", joinColumn="model_id" )
dim = IntCol( default=None )
def __iter__( self ):
return self.scales.orderBy( "feature_id" ).__iter__()
def theFeatures( self, image, verbosity=3 ):
if verbosity > 2:
print "[ScaleModel.theFeatures]", self.id
print image
fv = list(ScaledFeatureValue.getFeatures(
scalemodel=self,image=image,verbosity=verbosity))
N = len(fv)
if N < self.getDim():
if verbosity > 1:
print "[ScaleModel.theFeatures] Missing data (%s/%s)." % (
N, self.getDim())
print image
print self
raise MissingDataException
elif N > self.getDim():
raise Exception, "Too many feature values retrieved."
return fv
[docs] def destroy(self,values=False):
"Delete the model from the data base."
for s in self.scales:
s.destroySelf()
print "[destroy]", self
self.destroySelf()
[docs] def getDim(self):
"Return the feature space dimensionality."
if self.dim == None:
self.dim = self.scales.count()
return self.dim
@classmethod
def calc( cls, fv, testset, verbosity=0 ):
S = cls( testset=testset )
S.calculate( fv, testset, verbosity=verbosity )
return S
def calculate( self, fv, testset, verbosity=0 ):
if self.dim == None:
self.dim = 0
for f in fv:
(factor,addterm) = calcScale( testset, f, verbosity=verbosity )
try:
Scaling( model=self, feature=f, factor=factor, addterm=addterm )
self.dim += 1
except StandardError as e:
if not isDuplicateError(e):
print "[Scaling] Unknown error."
raise
S = Scaling.selectBy( svmodel=self, feature=f )
if verbosity > 0:
print "[Scaling] Record already exists."
print S
S.factor = factor
S.addterm = addterm
return
[docs]class Scaling(SQLObject):
"The Scaling object holds the model to scale a particular feature."
feature = ForeignKey( "Feature" )
model = ForeignKey( "ScaleModel", cascade=True )
idx = DatabaseIndex( 'model', 'feature', unique=True )
factor = FloatCol( default=1 )
addterm = FloatCol( default=0 )
def calcScale( tset, feature, verbosity=0 ):
fv = tset.images.throughTo.features.filter(
FeatureValue.q.feature == feature )
mn = fv.min("value")
mx = fv.max("value")
if mn == None or mx == None:
if fv.count() == 0:
if verbosity > 1:
print "[calcScale] Missing data."
raise MissingDataException, "Features have not been extracted."
else:
raise Exception, "Unknown error: min or max returns None."
(lower, upper) = (-1,+1)
range = upper - lower
factor = range/(mx-mn)
addterm = (mx+mn)/2
if verbosity > 2:
print feature, (mn,mx)
print feature, (factor,addterm)
return (factor,addterm)
[docs]class ScaledFeatureValue(SQLObject):
"""This is an attempt to start on an object to fetch multiple
SQL records in one operation to save time. NOT COMPLETE.
"""
class sqlmeta:
fromDatabase = True
columnList = True
idName = "id"
idType = str
#key = StringCol()
#value = FloatCol()
#rawvalue = FloatCol()
#addterm = FloatCol()
#factor = FloatCol()
#image = ForeignKey("Image")
#scalemodel = ForeignKey("ScaleModel")
@classmethod
def getFeatures(cls,image,scalemodel,verbosity=0):
try:
iid = image.id
except AttributeError:
iid = image
try:
sid = scalemodel.id
except AttributeError:
sid = scalemodel
if verbosity > 2:
print "[ScaledFeatureValue.getFeatures]", iid, sid
return cls.selectBy( imageID=iid, scalemodelID=sid )
def getFID(self):
return self.fid
def __cmp__(self,r):
return cmp(self.getFID(),r.getFID())
[docs] def getValue(self):
"Return the scaled feature value."
return self.value