From ce5a4b372ff4350fc3dfeb3233824dca73ded780 Mon Sep 17 00:00:00 2001 From: unknown <Ed@.(none)> Date: Tue, 30 Aug 2011 23:40:38 -0400 Subject: [PATCH] Created a git repository --- __init__.py | 0 filelist/__init__.py | 0 filelist/filesettings.py | 9 + filelist/filetools.py | 82 +++++ filelist/models.py | 207 +++++++++++ filelist/odysseus/imageio.py | 324 ++++++++++++++++ filelist/odysseus/imageprocess.py | 590 ++++++++++++++++++++++++++++++ filelist/populatedb.py | 35 ++ filelist/views.py | 170 +++++++++ htroot/filter.htm | 54 +++ htroot/processing.htm | 221 +++++++++++ settings.py | 98 +++++ templates/imglist.html | 24 ++ templates/methodlist.html | 18 + templates/paramlist.html | 15 + templates/runloglist.html | 47 +++ templates/sampleimages.html | 3 + urls.py | 31 ++ 18 files changed, 1928 insertions(+) create mode 100644 __init__.py create mode 100644 filelist/__init__.py create mode 100644 filelist/filesettings.py create mode 100644 filelist/filetools.py create mode 100644 filelist/models.py create mode 100644 filelist/odysseus/imageio.py create mode 100644 filelist/odysseus/imageprocess.py create mode 100644 filelist/populatedb.py create mode 100644 filelist/views.py create mode 100644 htroot/filter.htm create mode 100644 htroot/processing.htm create mode 100644 settings.py create mode 100644 templates/imglist.html create mode 100644 templates/methodlist.html create mode 100644 templates/paramlist.html create mode 100644 templates/runloglist.html create mode 100644 templates/sampleimages.html create mode 100644 urls.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/filelist/__init__.py b/filelist/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/filelist/filesettings.py b/filelist/filesettings.py new file mode 100644 index 0000000..9e8d82b --- /dev/null +++ b/filelist/filesettings.py @@ -0,0 +1,9 @@ +IMG_LOCS = {"cimages":"C:\\images\\"} +RL_LOCS = {"crunlogs":"C:\\runlogs\\"} +STATIC_DIR = "./static/" +#THUMB_DIR = "C:\\thumb\\" +PNG_DIR = "C:\\django\\django\\clinamen\\static\\png\\" +PNG_URL = "../../../static/png/" +THUMB_DIR = "C:\\django\\django\\clinamen\\static\\thumb\\" +THUMB_URL = "../../../static/thumb/" +THUMB_SIZE = 64 diff --git a/filelist/filetools.py b/filelist/filetools.py new file mode 100644 index 0000000..ec76ecc --- /dev/null +++ b/filelist/filetools.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +"""Functions to easily process file names.""" + +import os +import glob +import re + + +def sort_files_by_date(filelist, newestfirst=True): + """Return a list of files sorted by time, newest first by default""" + + mod_time_file = [(os.lstat(item).st_mtime, item) for item in filelist] + + if newestfirst: + mod_time_file.sort(reverse=True) + else: + mod_time_file.sort() + + return [file[1] for file in mod_time_file] + + +def get_files_in_dir(dirname, ext='TIF', globexpr=None, sort=True): + """Return a list of all files in a directory with extension ext + + When ``globexpr`` is given, ``ext`` is ignored and the Python glob module + is used to search for all files with the given pattern. + + **Inputs** + + * dirname: string, full path to the directory + * ext: string, extension of the files to process + * globexpr: string, glob search expression (can contain wildcards) + * sort: bool, if True the results are sorted by file date/time, newest + first + + **Outputs** + + * imgs: list of strings, each string in the list is the complete path to + a file + + """ + + if globexpr: + imgs = glob.glob(os.path.join(dirname, ''.join(globexpr))) + else: + imgs = glob.glob(os.path.join(dirname, ''.join(['*.', ext]))) + if sort: + imgs = sort_files_by_date(imgs) + + return imgs + + +def find_imgnames(imglist, startstr, stopstr): + """Finds names from imglist between startstr and stopstr in time-ordered way + + **Inputs** + + * imglist: list of str, containing paths of images on disc + * startstr: str, part of the name of the oldest image by date that is + wanted + * stopstr: str, part of the name of the newest image by date that is + wanted + + **Outputs** + + * imgs: list of str, containing the found paths to image files + + """ + + imgs = sort_files_by_date(imglist, newestfirst=False) + for img in imgs: + found_one = re.search(startstr, img) + if found_one: + start_idx = imgs.index(img) + break + for img in imgs: + found_one = re.search(stopstr, img) + if found_one: + stop_idx = imgs.index(img) + break + + return imgs[start_idx:stop_idx+1] diff --git a/filelist/models.py b/filelist/models.py new file mode 100644 index 0000000..b1bc608 --- /dev/null +++ b/filelist/models.py @@ -0,0 +1,207 @@ +import os +import numpy as np +import scipy as sp +import re +import inspect +import collections +#from odysseus.imageio import imgimport_intelligent, list_of_frames +os.environ['DJANGO_SETTINGS_MODULE'] = "settings" +from django.db import models +import filelist.filesettings as filesettings +import Image + +class RunLogInfo(models.Model): + #path = models.FilePathField(filesettings.RL_DIR, primary_key=True) + path = models.CharField(max_length=200, primary_key=True) + loc_key = models.CharField(max_length=100) + time = models.DateTimeField() + sequencepath = models.CharField(max_length=200) + listiterationnumber = models.IntegerField(null=True, blank=True) + liststarttime = models.DateTimeField(null=True,blank=True) + sequenceduration = models.FloatField(null=True,blank=True) + description = models.TextField(null=True, blank=True) + exceptions = models.TextField(null=True, blank=True) + class Meta: + ordering=['-time'] + +class VariableValue(models.Model): + name = models.CharField(max_length=30) + value = models.FloatField() + runlog = models.ForeignKey('RunLogInfo') + +class ImageInfo(models.Model): + #path = models.FilePathField(filesettings.IMG_DIR, primary_key=True) + path = models.CharField(max_length=100, primary_key=True) + loc_key = models.CharField(max_length=100) + time = models.DateTimeField() + height = models.IntegerField(null=True, blank=True) + width = models.IntegerField(null=True, blank=True) + number_of_frames = models.IntegerField(null=True, blank=True) + imgtype = models.ForeignKey('ImageType', null=True, blank=True) + runlog = models.ForeignKey('RunLogInfo', null=True, blank=True) + def makeRawFrames(self): + frames = np.dstack(list_of_frames(self.path)) + + self.width = np.size(frames,0) + self.height = np.size(frames,1) + self.number_of_frames = np.size(frames,2) + + for ii in range(np.size(frames,2)): + filename = os.path.splitext(os.path.split(self.path)[1])[0]+'_'+str(ii)+'.png' + + newframe = RawFrame(sourceimage=self, framenumber=ii) + newframe.saveframe(frames[:,:,ii], filename) + newframe.save() + def deleteProcFrames(self): + ProcessedFrame.objects.filter(sourceimage=self).delete() + def ProcessImage(self): + for method in self.imgtype.methods.all(): + procmodule = __import__(method.modulename) + procmethod = getattr(procmodule, method.name) + argdict = {} + for param in TypeParameters.objects.filter(imagetype=self.imgtype, methodargument__method=method): + argdict[param.methodargument.name]=param.value + for ROIparam in TypeROI.objects.filter(imagetype=self.imgtype, methodargument__method=method): + argdict[ROIParam.methodargument.name]=ROIparam.ToDict() + result=procmethod(self.path, **argdict) + if not isinstance(result,tuple): + result = (result,) + for ii in range(len(result)): + if isarray(result[ii]): + filename=os.path.splitext(os.path.split(self.path)[1])[0]+'_'+method.name+'_'+str(ii)+'.png' + + newrecord = ProcessedFrame(sourceimage=self, method=method, framenumber=ii) + newrecord.saveframe(result[ii], filename) + newrecord.save() + else: + newrecord = ProcessedValue(sourceimage=self, method=method, value=float(item[ii]), index=ii) + newrecord.save() + def getClosestSequence(self): + sql = "SELECT * FROM filelist_runloginfo ORDER BY ABS(TIMESTAMPDIFF(SECOND, time,'" + self.time.strftime('%Y-%m-%d %H:%M:%S') + "')) LIMIT 1" + for retrunlog in RunLogInfo.objects.raw(sql): + self.runlog = retrunlog + def getTypeFromDescription(self): + matchObj = re.search("imgtype=(\w+)", self.runlog.description) + if matchObj: + self.imgtype= matchObj.group(1) + class Meta: + ordering=['-time'] + +class FrameInfo(models.Model): + pngpath = models.CharField(max_length=100, primary_key=True) + pngurl = models.CharField(max_length=100) + pngheight = models.IntegerField() + pngwidth = models.IntegerField() + thumbpath = models.CharField(max_length=100) + thumburl = models.CharField(max_length=100) + thumbheight = models.IntegerField() + thumbwidth = models.IntegerField() + framenumber = models.IntegerField(null=True, blank=True) + sourceimage = models.ForeignKey('ImageInfo', null=True, blank=True) + def saveframe(self, frame, filename): + self.pngpath=os.path.join(filesettings.PNG_DIR,filename) + self.pngurl=filesettings.PNG_URL+filename + self.thumbpath=os.path.join(filesettings.THUMB_DIR,filename) + self.thumburl=filesettings.THUMB_URL+filename + + im=sp.misc.toimage(frame) + im.save(self.pngpath) + + self.pngwidth = np.size(frame,0) + self.pngheight = np.size(frame,1) + aspect=float(self.pngwidth)/float(self.pngheight) + + if aspect>1: + self.thumbwidth = filesettings.THUMB_SIZE + self.thumbheight = int(filesettings.THUMB_SIZE/aspect) + else: + self.thumbwidth = int(filesettings.THUMB_SIZE * aspect) + self.thumbheight = filesettings.THUMB_SIZE + im.thumbnail([self.thumbwidth, self.thumbheight], Image.ANTIALIAS) + im.save(self.thumbpath) + class Meta: + abstract=True + ordering=['framenumber'] + +class ProcessingMethod(models.Model): + modulename = models.CharField(max_length=100) + name = models.CharField(max_length=30) + def getargs(self): + procmodule = __import__(self.modulename) + #procmethod = procmodule.getAttr(procmodule, self.name) + procmethod = getattr(procmodule, self.name) + argspec = inspect.getargspec(procmethod) + for keyword in argspec.args[1:]: + newmetharg, created = MethodArgument.objects.get_or_create(method=self, name=keyword) + if re.search("roi_(\w+)", keyword): + newmetharg.isROI = True + newmetharg.save() + class Meta: + unique_together = ("modulename", "name") + +class MethodArgument(models.Model): + method = models.ForeignKey('ProcessingMethod') + name = models.CharField(max_length=30) + isROI = models.NullBooleanField(null=True, blank=True) + +class ImageType(models.Model): + name = models.CharField(max_length=30, primary_key=True) + methods = models.ManyToManyField('ProcessingMethod') + parameters = models.ManyToManyField('MethodArgument', through='TypeParameters') + sample = models.ForeignKey('RawFrame', null=True, blank=True) + def ClearProcessed(self): + ProcessedFrame.objects.filter(sourceimage__imgtype=self).delete() + def ProcessType(self): + #imtype = ImageType.objects.get(name=self.name) + #for iminfo in imtype.ImageInfo_set.all(): + for iminfo in self.imageinfo_set.all(): + iminfo.ProcessImage() + +class TypeParameters(models.Model): + imagetype = models.ForeignKey('ImageType') + methodargument = models.ForeignKey('MethodArgument') + value = models.FloatField() + class Meta: + unique_together = ("imagetype", "methodargument") + +class TypeROI(models.Model): + imagetype = models.ForeignKey('ImageType') + methodargument = models.ForeignKey('MethodArgument') + x1 = models.IntegerField(null=True, blank=True) + x2 = models.IntegerField(null=True, blank=True) + y1 = models.IntegerField(null=True, blank=True) + y2 = models.IntegerField(null=True, blank=True) + def ToDict(self): + return {"x1": x1, "x2": x2, "y1": y1, "y2": y2} + class Meta: + unique_together = ("imagetype", "methodargument") + +class ProcessedFrame(FrameInfo): + method = models.ForeignKey('ProcessingMethod', null=True, blank=True) + +class RawFrame(FrameInfo): + class Meta: + abstract=False + +class ProcessedValue(models.Model): + sourceimage = models.ForeignKey('ImageInfo') + method = models.ForeignKey('ProcessingMethod', null=True, blank=True) + value = models.FloatField() + index = models.IntegerField(null=True, blank=True) + class Meta: + unique_together = ("sourceimage", "method") + +def get_iterable(x): + if isinstance(x, collections.Iterable): + return x + else: + return (x,) + +def isarray(s): + try: + len(s) + return True + except TypeError: + return False + + \ No newline at end of file diff --git a/filelist/odysseus/imageio.py b/filelist/odysseus/imageio.py new file mode 100644 index 0000000..5f4f065 --- /dev/null +++ b/filelist/odysseus/imageio.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +"""I/O functions for several image formats. + +The relevant formats are TIF, hdf5 and ascii. For ascii and binary numpy formats +no separate functions are provided for saving an image. This is because saving +in these formats requires just a single command: +ascii: np.savetxt('filename', img) +binary (.npy): np.save('filename', img) + +""" + +import os + +import numpy as np +import scipy as sp +# if available, use Zach Pincus' pil_lite which has correct 16-bit TIFF loading +try: + import pil_lite.pil_core.Image as Image +except ImportError: + import Image +#import tables + + +def list_of_frames(img_name): + """Return the list of frames for an image file. + + Details are as described in the imgimport_intelligent docstring. + + """ + + img = Image.open(img_name) + imglist = [] + + try: + for i in xrange(8): + if img.mode == 'I': + imdata = np.asarray(img, dtype=np.int16) + else: + imdata = np.asarray(img, dtype=np.float32) + # fix 3-channel TIFF images + if np.rank(imdata)==3: + imdata = imdata[:,:,0] + 256*imdata[:,:,1] + 65536*imdata[:,:,2] + imglist.append(imdata) + img.seek(i+1) # next frame + except EOFError: + pass + + if not imglist: + raise ImportError, 'No frames detected in file %s' %img_name + else: + return imglist + + +def imgimport_intelligent(img_name, foo=3, bar=4, roi_baz=[12,24,56,78]): + """Opens an image file containing one or more frames + + The number of frames in the image is automatically detected. If it is a + single frame, it is assumed to be a transmission image. If there are three + frames, the first one is assumed to be probe with atoms (pwa), the second + one probe without atoms (pwoa) and the third one a dark field (df). + For four frames, it is assumed to be (pwoa, pwa, df1, df2). + For six frames, the first two are discarded (they are for clearing the + CCD charge on the Coolsnap camera), three to six are (pwoa, pwa, df1, df2). + + **Inputs** + + * img_name: string containing the full path to an image + + **Outputs** + + * img_array: 3D array, containing the three or four frames of the image, + in the order (pwa, pwoa, df, df2). + + **Notes** + + The datatype has to be set to float in Winview, otherwise there is a + strange problem reading the frames; support for 16-bit tif files is + lacking a bit in PIL. Note: when pil_lite is available this does work. + + The same support is lacking in MS .Net apparently, hence the weird check + for 3-channel TIFFs. What happens here is that XCamera can output multipage + 8-bit RGB TIFFs. Each page is of shape (M,N,3), where the 8-bit color + channels combine to output 24-bit B/W data. + + """ + + imglist = list_of_frames(img_name) + + if len(imglist)==1: + return imglist[0] + elif len(imglist) in [3, 4]: + # make an array from the list of frames, with shape (img[0], img[1], 3) + img_array = np.dstack(imglist) + elif len(imglist)==6: + # get rid of first two frames, they're junk. then swap pwoa, pwa. + img_array = np.dstack([imglist[3], imglist[2], imglist[5], imglist[4]]) + elif len(imglist)==8: + # get rid of first two frames, then 2x PWA, 2x DF, 2x PWOA (swap DF, PWOA) + img_array = np.dstack([imglist[2], imglist[3], imglist[6], imglist[7], + imglist[4], imglist[5]]) + else: + raise ImportError, 'Number of frames is %s' %(len(imglist)) + + return img_array + + +def import_rawframes(img_name): + """Opens an image file containing three frames + + The datatype has to be set to float in Winview, otherwise there is a + strange problem reading the frames; support for 16-bit tif files is + lacking a bit in PIL. + + **Inputs** + + * img_name: string containing the full path to an image + + **Outputs** + + * img_array: 3D array, containing the three frames of the image + + """ + + img = Image.open(img_name) + # note the reversed order because Image and asarray have reversed order + img_array = np.zeros((img.size[1], img.size[0], 3), dtype=np.float32) + + img_array[:, :, 0] = np.asarray(img, dtype=np.float32) + try: + img.seek(1) # next frame + img_array[:, :, 1] = np.asarray(img, dtype=np.float32) + img.seek(2) # next frame + img_array[:, :, 2] = np.asarray(img, dtype=np.float32) + except EOFError: + print 'This image contains less than 3 frames' + return None + + return img_array + + +def import_rawimage(img_name): + """Opens an image file and returns it as an array.""" + + im = Image.open(img_name) + + return np.asarray(im) + + +def import_xcamera(img_name, ext='xraw'): + """Load the three .xraw files from XCamera + + It is assumed that the file with extension .xraw0 contains the probe + with atoms (pwa), the one with extension .xraw1 the probe without atoms + (pwoa), and the one with extension .xraw2 the dark field (df). + + **Inputs** + + * img_name: str, name of the image with or without extension + (the extension is stripped and replaced by `ext`. + * ext: str, the extension of the XCamera file. Normally xraw or xroi. + + **Outputs** + + * raw_array: 3D array, containing the three raw frames (pwa, pwoa, df) + + """ + + if ext=='xraw': + rawext = ['.xraw0', '.xraw1', '.xraw2'] + elif ext=='xroi': + rawext = ['.xroi0', '.xroi1', '.xroi2'] + else: + raise ValueError, 'Unknown extension for XCamera file' + + basename = os.path.splitext(img_name)[0] + try: + pwa = np.loadtxt(''.join([basename, rawext[0]]), dtype=np.int16) + pwoa = np.loadtxt(''.join([basename, rawext[1]]), dtype=np.int16) + df = np.loadtxt(''.join([basename, rawext[2]]), dtype=np.int16) + except IOError, e: + print e + return None + + raw_array = np.dstack([pwa, pwoa, df]) + + return raw_array + + +def save_tifimage(imgarray, fname, dirname=None): + """Save a single image in TIF format + + **Inputs** + + * imgarray: 2D array, containing a single frame image + * fname: str, filename of the file to save, optionally including + the full path to the directory + * dirname: str, if not None, fname will be appended to dirname to + obtain the full path of the file to save. + + **Notes** + + Multiple frame tif images are not supported. For such data hdf5 is the + recommended format. + + """ + + if dirname: + fname = os.path.join(dirname, fname) + fname = ''.join([os.path.splitext(fname)[0], '.tif']) + + im = sp.misc.toimage(imgarray, mode='F') + im.save(fname, mode='F') + + +def save_hdfimage(imgarray, fname, dirname=None): + """Save an image to an hdf5 file + + **Inputs** + + * imgarray: ndarray, containing the image data. If the array is 2D, + it is assumed that this is a single frame image. If it is + 3D, the frames will be saved as separate arrays: + ('pwa', 'pwoa', 'df'), and if there is a fourth frame this + is df2. + * fname: str, filename of the file to save, optionally including + the full path to the directory + * dirname: str, if not None, fname will be appended to dirname to + obtain the full path of the file to save. + + """ + + if dirname: + fname = os.path.join(dirname, fname) + fname = ''.join([os.path.splitext(fname)[0], '.h5']) + + # Open a new empty HDF5 file + h5file = tables.openFile(fname, mode='w') + + if len(imgarray.shape)==2: + # Get the root group + root = h5file.root + # Save image in the HDF5 file + h5file.createArray(root, 'img', imgarray, title='Transmission image') + + elif len(imgarray.shape)==3: + # image frames to be saved + pwa = imgarray[:, :, 0] + pwoa = imgarray[:, :, 1] + df = imgarray[:, :, 2] + # Get the root group + root = h5file.createGroup("/", 'rawframes', 'The raw image frames') + # Save image in the HDF5 file + h5file.createArray(root, 'pwa', pwa, title='Probe with atoms') + h5file.createArray(root, 'pwoa', pwoa, title='Probe without atoms') + h5file.createArray(root, 'df', df, title='Dark field') + + if imgarray.shape[2] > 3: + df2 = imgarray[:, :, 3] + h5file.createArray(root, 'df2', df2, title='Dark field 2') + + else: + print 'imgarray does not have the right dimensions, shape is: ', \ + imgarray.shape + + h5file.close() + + +def load_hdfimage(fname, dirname=None, ext_replace=True): + """Load an image from an hdf5 file + + **Inputs** + + * fname: str, filename of the file to save, optionally including + the full path to the directory + * dirname: str, if not None, fname will be appended to dirname to + obtain the full path of the file to save. + * ext_replace: bool, if True replaces the extension of fname with `.h5` + + **Outputs** + + * transimg: ndarray, the image data + + """ + + if dirname: + fname = os.path.join(dirname, fname) + if ext_replace: + fname = ''.join([os.path.splitext(fname)[0], '.h5']) + + h5file = tables.openFile(fname, mode='r') + + try: + transimg = np.asarray(h5file.root.img) + return transimg + + except tables.NoSuchNodeError: + pwa = np.asarray(h5file.root.rawframes.pwa) + pwoa = np.asarray(h5file.root.rawframes.pwoa) + df = np.asarray(h5file.root.rawframes.df) + imgarray = np.dstack([pwa, pwao, df]) + return imgarray + + finally: + h5file.close() + + +def convert_xcamera_to_hdf5(imglist, ext='xraw'): + """Convert every file in imglist to an hdf5 file. + + The raw files are saved in the hdf5 file as + `root.rawframes.pwa`, `root.rawframes.pwoa`, `root.rawframes.df`. + Their dtype is uint16, which results in files of a third smaller than + the xcamera text files. + + **Inputs** + + * imglist: list of str, paths to .xraw0 files + * ext: str, the extension of the XCamera file. Normally xraw or xroi. + + """ + + for img in imglist: + imgarray = import_xcamera(img, ext=ext) + save_hdfimage(imgarray, img) diff --git a/filelist/odysseus/imageprocess.py b/filelist/odysseus/imageprocess.py new file mode 100644 index 0000000..99587b6 --- /dev/null +++ b/filelist/odysseus/imageprocess.py @@ -0,0 +1,590 @@ +#!/usr/bin/env python +"""Image processing functions + +Some functionality is independent of the type of image, for example +smoothing, thresholding and interpolation. Other functionality is specific +to cold atom experiments, for example calculating optical density and +transmission for absorption images. + +""" + +import os + +import scipy as sp +import scipy.ndimage as ndimage +import numpy as np +import Image +import matplotlib as mpl +import pylab + + +def trans2od(transimg, maxod=3.5): + """Calculates the optical density image from a transmission image + + For pixels with strange values due to noise, replace the value of that pixel + by the maximum OD that can be experimentally measured. + + """ + + odimg = np.where(transimg>np.exp(-maxod), -np.log(transimg), maxod) + + return odimg + + +def od2trans(odimg, maxod=3.5): + """Calculates the transmission image from an optical density image + + For pixels with strange values due to noise, replace the value of that pixel + by the maximum OD that can be experimentally measured. + + """ + + transimg = np.where(odimg<maxod, np.exp(-odimg), np.exp(-maxod)) + + return transimg + + +def calc_absimage(raw_frames, norm_edge=0.): + """Calculates the transmission image and optical density. + + **Inputs** + + * raw_frames: 3D array, containing three or four images; + probe with atoms (pwa), probe without atoms (pwoa), + dark field (df) and `optionally` a second dark field (df2). + If there is no second dark field, the same one is used + twice. + * norm_edge: bool, if True, normalize to one using the edge (it is assumed + no atoms are visible on the edge. + + **Outputs** + + * transimg: 2d array containing the transmission image, + defined as (pwa - df)/(pwoa - df2). + * odimg: 2d array containing the optical density for each pixel + + """ + + pwa = raw_frames[:, :, 0] + pwoa = raw_frames[:, :, 1] + df = raw_frames[:, :, 2] + try: + df2 = raw_frames[:, :, 3] + except IndexError: + df2 = df + + nom = pwa - df + den = pwoa - df2 + nom = np.where(nom<1, 1, nom) + den = np.where(den<1, 1, den) + if norm_edge: + nom = normalize_edgestrip(nom) + den = normalize_edgestrip(den) + + transimg = nom.astype(float)/den + odimg = -np.log(transimg) + + return transimg, odimg + + +def threshold_image(img, thres=0.5, below=True): + """Returns a binary array (ones and zeros) depending on pixel values. + + **Inputs** + + * img: array, containing an image (also works for non-image data) + * thres: scalar value, the threshold value + * below: boolean value, True means that each element of img that is below + thres gives a 1 in the thresholded image, and each element that + is above it a 0. + + **Outputs** + + * thres_img: array, containing ones and zeros as a result of + thresholding the input array. + + """ + + if below: + thres_img = np.where(img<thres, 1, 0) + else: + thres_img = np.where(img<thres, 0, 1) + + return thres_img + + +def find_fitrange(od_prof, od_max=1, min_cutoff=8): + """Select a suitable range of radii to use for fitting the image. + + When the optical density saturates at a certain range of radii, and then + that data range is used for fitting, it throws off the fit. Therefore + a cutoff value for the maximum optical density should be specified, and the + fit only done for values of OD smaller than that. The data is smoothed, + and the index for the radius where the OD drops below OD_max is determined. + + **Inputs** + + * od_prof: 1D array, containing the radially averaged optical density + profile. + + **Outputs** + + * cutoff: int, the larger value of index of rcoord where od_prof<od_max + or min_cutoff. + + **Optional inputs** + + * od_max: float, the maximum desired value of the optical density + * min_cutoff: int, the minimum value for the cutoff index. The reason to + use this is that a radially averaged profile is very noisy + around the center which may skew a fit. + + """ + try: + od_new = smooth(od_prof, window_len=15) + cutoff = mpl.mlab.find(od_new<od_max).min() + except ValueError: + # no values below the cutoff + cutoff = min_cutoff + + cutoff = max(cutoff, min_cutoff) + if cutoff > od_prof.size: + raise ValueError, 'Input array is smaller than min_cutoff' + + return cutoff + + +def radial_interpolate(img, com, dr, phi=None, elliptic=None, full_output=False): + """Does radial averaging around the center of mass of the image. + + Radial averaging of the image data on circles spaced by dr around the + center of mass. The number of points on each circle is dphi*sqrt(i+1), + with i the circle index. A bilinear interpolation method is used. + + **Inputs** + + * img: 2D array, normally containing image data + * com: 1D array with two elements, the center of mass coordinates in + pixels + * dr: radial step size in pixels + + **Outputs** + + * rcoord: 1D array containing the radial coordinate + * rad_profile: 1D array containing the averaged profile + + **Optional inputs** + + * phi: 1D array, the angles along which line profiles are taken. More + values in phi means a more precise radial average; default is + 2*pi times the maximum radius in pixels + * elliptic: tuple, containing two elements. the first one is the + ellipticity (or ratio of major and minor axes), the second one is the + angle by which the major axis is rotated from the y-axis. + * full_output: bool, selects whether rprofiles and phi are returned + + """ + + xsize, ysize = img.shape + # number of used points in radial direction, stops if image edge is reached + rmax = np.array([xsize-com[0], com[0], ysize-com[1], com[1]]).min() + rcoord = np.arange(0, rmax, dr) + if not phi: + phi = np.linspace(0, 2*np.pi, rmax*np.pi) + + rprofiles = lineprofiles(img, com, rcoord, phi, elliptic=elliptic) + rad_profile = rprofiles.mean(axis=1) + + if full_output: + return rcoord, rad_profile, rprofiles, phi + else: + return rcoord, rad_profile + + +def lineprofiles(img, com, rcoord, phi, elliptic=None): + """Generate radial profiles around center of mass + + Line profiles without any averaging are generated. This is useful for + comparing the radially averaged profile with, to make sure that that is a + valid procedure. + + **Inputs** + + * img: 2D array, normally containing image data + * com: 1D array with two elements, the center of mass coordinates in pixels + * rcoord: 1D array, radial coordinate for line profiles + this is usually obtained from radial_interpolate + * phi: 1D array, angles along which line profiles are required + + **Outputs** + + * rprofiles: 2D array, containing radial profiles along angles + + **Optional inputs** + + * elliptic: tuple, containing two elements. the first one is the + ellipticity (or ratio of major and minor axes), the second one is the + angle by which the major axis is rotated from the y-axis. This should + be the same as used for radial averaging. + + **Notes** + + The form used for mapping an ellipse to (x,y) coordinates is: + x = a\cos\phi\cos\alpha - b\sin\phi\sin\alpha + y = b\sin\phi\cos\alpha + a\cos\phi\sin\alpha + + """ + + indshape = (phi.size, rcoord.size) + + if elliptic: + (ell, rot) = elliptic + else: + (ell, rot) = (1, 0) + + xr = com[0] + (np.ones(indshape)*rcoord).transpose()*np.cos(phi)*np.cos(rot) - \ + ell*(np.ones(indshape)*rcoord).transpose()*np.sin(phi)*np.sin(rot) + yr = com[1] + ell*(np.ones(indshape)*rcoord).transpose()*np.sin(phi)*np.cos(rot) - \ + (np.ones(indshape)*rcoord).transpose()*np.cos(phi)*np.sin(rot) + + rprofiles = bilinear_interpolate(xr, yr, img) + + return rprofiles + + +# move out the plotting part! +def radialprofile_errors(odprofiles, angles, od_prof, od_cutoff, \ + showfig=False, savefig_name=None, report=True): + """Calculate errors in radial profiles as a function of angle + + **Inputs** + + * odprofiles: 2D array, containing radial OD profiles along angles + * angles: 1D array, angles at which radial profiles are taken + (zero is postive x-axis) + * od_prof: 1D array, radially averaged optical density + * od_cutoff: integer, index of profiles at which maximum fit-OD is reached + + **Outputs** + + * av_err: float, sum of absolute values of errors in errsum + + **Optional inputs** + + * showfig: bool, determines if a figure is shown with density profile + and fit + * report: bool, if True print the sums of the mean and rms errors + * savefig_name: string, if not None and showfig is True, the figure is + not shown but saved as png with this string as filename. + + """ + + err = (odprofiles[od_cutoff:, :].transpose() - \ + od_prof[od_cutoff:]).sum(axis=1) + + av_err = np.abs(err).mean() + + if report: + print 'mean error is ', err.mean() + print 'rms error is ', av_err + + if showfig: + # angular plot of errors, red for positive, blue for negative values + pylab.figure() + poserr = pylab.find(err>0) + negerr = pylab.find(err<0) + + pylab.polar(angles[negerr], np.abs(err[negerr]), 'ko', \ + angles[poserr], np.abs(err[poserr]), 'wo') + pylab.title('Angular dependence of fit error') + pylab.text(np.pi/2+0.3, np.abs(err).max()*0.85, \ + r'$\sum_{\phi}|\sum_r\Delta_{OD}|=%1.1f$'%av_err) + if not savefig_name: + pylab.show() + else: + pylab.savefig(''.join([os.path.splitext(savefig_name)[0], '.png'])) + + return av_err + + +def bilinear_interpolate(xr, yr, img): + """Do a bi-linear interpolation to get the value at image coordinates + + **Inputs** + * xr: array-like, the x-coordinates of the point to be interpolated + * yr: array-like, the y-coordinates of the point to be interpolated + * img: 2d-array, the image data + + **Outputs** + * ans: array-like, the result of the interpolation + + """ + + ans = ndimage.map_coordinates(img, np.array([xr, yr]), order=1, \ + mode='nearest') + return ans + + +def imgslice(img, cpoint, angle=0, width=None): + """Take a line profile through the centerpoint + + **Inputs** + * img: 2D array, the image data + * cpoint: 1D array, the center point coordinates of the required slice + + **Outputs** + * lprof_coord: 1D array, the slice indices in units of pixels + * lprof: 1D array, the slice data + + **Optional inputs** + + * angle: float, the angle under which the slice is taken in degrees + * width: float, the width over which the slice is averaged + + """ + + angle = angle*np.pi/180 # deg to rad + # determine coefficients a and b of center line y=ax+b + a = np.tan(angle) + b = cpoint[1] - cpoint[0]*a + # max of indices + xmax, ymax = img.shape + xmax = xmax-1 + ymax = ymax-1 + # determine begin and end points of center line + if 0 < b < ymax: + startpt = (0, b) + elif 0 < -b/a < xmax: + startpt = (-b/a, 0) + else: + startpt = ((ymax-b)/a, ymax) + if 0 < a*xmax+b < ymax: + stoppt = (xmax, a*xmax+b) + elif 0 < (ymax-b)/a < xmax: + stoppt = ((ymax-b)/a, ymax) + else: + stoppt = (-b/a, 0) + + def dist(a, b): + """Distance between points""" + d = a - b + return np.sqrt(np.dot(d,d)) + + slicelen = min(dist(startpt, cpoint), dist(stoppt, cpoint))*0.8 + + # generate the slice coordinates, discard endpoint (safer for interpolation) + npts = 3 + xslice = cpoint[0] + np.arange(-slicelen, slicelen, 1./npts)*np.cos(angle) + yslice = cpoint[1] + np.arange(-slicelen, slicelen, 1./npts)*np.sin(angle) + + if width: + try: + perpstep = np.linspace(-width/2., width/2., num=round(width)) + lprof = np.zeros((xslice.size, perpstep.size)) + for i in xrange(perpstep.size): + xx_slice = xslice + perpstep[i]*np.cos(angle+np.pi/2) + yy_slice = yslice + perpstep[i]*np.sin(angle+np.pi/2) + lprof[:, i] = bilinear_interpolate(xx_slice, yy_slice, img) + lprof = lprof.mean(axis=1) + except IndexError: + print 'Index out of bounds, may be an error in the if/else code above' + raise IndexError + else: + xslice = xslice[1:-1] + yslice = yslice[1:-1] + lprof = bilinear_interpolate(xslice, yslice, img) + + lprof_coord = np.arange(lprof.size)-lprof.size/2. + + return lprof_coord, lprof, npts + + +def mirror_line(linedata, negative_mirror=False): + """Mirrors a 1D array around its first element + + **Inputs** + + * linedata: 1D array, the array to be mirrored + * negative_mirror: bool, if True the mirrors elements are multiplied by + -1. This is useful to mirror the x-axis of a plot. + + **Outputs** + + * mirrored: 1D array, the output array, which is now symmetric around its + midpoint. + + """ + + mir_size = linedata.shape[0] -1 + mirrored = np.zeros(mir_size*2 + 1) + mirror_idx = np.arange(mir_size, 0, -1) + mirrored[:mir_size] = linedata[mirror_idx] + mirrored[mir_size:] = linedata + + if negative_mirror: + mirrored[:mir_size] *= -1 + + return mirrored + + +def smooth(x, window_len=10, window='hanning'): + """Smooth the data using a window with requested size. + + This method is based on the convolution of a scaled window with the signal. + The signal is prepared by introducing reflected copies of the signal + (with the window size) in both ends so that transient parts are minimized + in the begining and end part of the output signal. + + Adapted from the Scipy Cookbook by Ralf Gommers. + + **Inputs** + + * x: 1D array, data that needs to be smoothed + + **Outputs** + + * x_smooth: 1D array, the smoothed signal + + **Optional inputs** + + * window_len: int, the size of the smoothing window + * window: str, the type of window from 'flat', 'hanning', 'hamming', + 'bartlett', 'blackman'. A flat window will produce a + moving average smoothing. + + """ + + if x.ndim != 1: + raise ValueError, "smooth only accepts 1 dimension arrays." + + if x.size < window_len: + window_len = round(x.size/2) + + if window_len<3: + return x + + if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']: + raise ValueError, \ + """Window is on of 'flat', 'hanning', 'hamming', + 'bartlett', 'blackman'""" + + s = np.r_[2*x[0]-x[window_len:1:-1], x, 2*x[-1]-x[-1:-window_len:-1]] + + if window=='flat': # moving average + w = np.ones(window_len, 'd') + else: + w = eval('np.'+window+'(window_len)') + + y = np.convolve(w/w.sum(), s, mode='same') + x_smooth = y[window_len-1:-window_len+1] + + return x_smooth + + +def maxod_correct(odimg, odmax): + """Corrects calculated OD from an absorption image for finite OD_max + + This idea was taken from Brian DeMarco's thesis, but it does not seem to + make much of a difference at low OD. For high-OD images it causes errors + because there will be data points with measured OD higher than the maximum + observable OD due to noise in the image. + + It is left in here for completeness, but it is recommended to not use this + method. Instead, images should be taken in a regime where this correction + is negligibly small anyway (i.e. below an OD of 1.5). + + """ + + c = np.exp(odmax) - 1 + realOD = -np.log((c+1.)/c*np.exp(-odimg) - 1./c) + + return realOD + + +def normalize_img(img, com, size): + """Mask off the atoms, then fit linear slopes to the image and normalize + + We assume that there are no atoms left outside 1.5 times the size. This + seems to be a reasonable assumption, it does not influence the result of + the normalization. + + **Inputs** + + * img: 2D array, containing the image + * com: tuple, center of mass coordinates + * size: float, radial size of the cloud + + **Outputs** + + * normimg: 2D array, the normalized image + + """ + + xmax, ymax = img.shape + + # create mask + x_ind1 = round(com[0] - 1.5*size) + x_ind2 = round(com[0] + 1.5*size) + y_ind1 = round(com[1] - 1.5*size) + y_ind2 = round(com[1] + 1.5*size) + + # fit first order polynomial along x and y (do not use quadratic terms!!) + if x_ind1>0 and x_ind2<xmax and y_ind1>0 and y_ind2<ymax: + normx = np.zeros(x_ind1 + xmax - x_ind2, dtype=float) + xx = np.ones(normx.shape) + xx[:x_ind1] = np.arange(x_ind1) + xx[x_ind1:] = np.arange(x_ind2, xmax) + normx[:x_ind1] = img[:x_ind1, :].mean(axis=1) + normx[x_ind1:] = img[x_ind2:, :].mean(axis=1) + + # fit normx vs xx + fitline = np.polyfit(xx, normx, 1) + + divx = np.ones(img.shape, dtype=float).transpose()*\ + np.polyval(fitline, np.arange(img.shape[0])).transpose() + normimg = img/divx.transpose() + + normy = np.zeros(y_ind1 + ymax - y_ind2, dtype=float) + yy = np.ones(normy.shape) + yy[:y_ind1] = np.arange(y_ind1) + yy[y_ind1:] = np.arange(y_ind2, ymax) + normy[:y_ind1] = normimg[:, :y_ind1].mean(axis=0) + normy[y_ind1:] = normimg[:, y_ind2:].mean(axis=0) + + # fit normx vs yy + fitline = np.polyfit(yy, normy, 1) + + divy = np.ones(normimg.shape, dtype=float)*\ + np.polyval(fitline, np.arange(normimg.shape[1])) + normimg = normimg/divy + else: + print "atom cloud extends to the edge of the image, can't normalize" + raise NotImplementedError + + return normimg + + +def normalize_edgestrip(img, normval=1., striplen=5): + """Normalizes the image so the average value on the edges is normval. + + This is simply a multiplication of the whole image array by a number + so that the average intensity on the edges of the image is `normval`. + + **Inputs** + + * img: 2D array, image data + * normval: float, the value to which img is normalized + * striplen: int, number of pixels along each edge used for normalization + + **Outputs** + + * normimg: 2D array, the normalized image + + """ + + vstrip = (img[:striplen, :] + img[-striplen:, :])*0.5 + hstrip = (img[:, :striplen] + img[:, -striplen:])*0.5 + normfactor = (hstrip.sum() + vstrip.sum())/(hstrip.size + vstrip.size) + normimg = img*normval/normfactor + + return normimg diff --git a/filelist/populatedb.py b/filelist/populatedb.py new file mode 100644 index 0000000..6099553 --- /dev/null +++ b/filelist/populatedb.py @@ -0,0 +1,35 @@ +import filetools +import os +import re +import glob +import models +import datetime +import filesettings +import scipy as sp +import numpy as np +from odysseus.imageio import imgimport_intelligent +from odysseus.imageprocess import calc_absimage +import Image +import collections + +def fullupdateimages(): + for ikey, iloc in filesettings.IMG_LOCS.iteritems(): + for imgpath in filetools.get_files_in_dir(iloc): + mod_time_file = os.lstat(imgpath).st_mtime + mod_datetime = datetime.datetime.fromtimestamp(mod_time_file) + imginfo=models.ImageInfo(path=imgpath, time=mod_datetime, loc_key=ikey) + imginfo.save() + imginfo.makeRawFrames() + imginfo.getClosestSequence() + imginfo.save() + imginfo.ProcessImage + imginfo.save() + +def getClosestSequence(imginfo): + sql = "SELECT * FROM filelist_runloginfo ORDER BY ABS(TIMESTAMPDIFF(SECOND, time,'" + imginfo.time.strftime('%Y-%m-%d %H:%M:%S') + "')) LIMIT 1" + for retrunlog in models.RunLogInfo.objects.raw(sql): + imginfo.runlog = retrunlog + + + + \ No newline at end of file diff --git a/filelist/views.py b/filelist/views.py new file mode 100644 index 0000000..d9c94f3 --- /dev/null +++ b/filelist/views.py @@ -0,0 +1,170 @@ +# Create your views here. +from django.shortcuts import render_to_response, get_list_or_404 +from django.http import HttpResponse +from clinamen.filelist.models import * +import datetime +import json + +def tester(request): + return HttpResponse("Test succeeded again") + +def imagelist(request, url): + img_list = get_list_or_404(ImageInfo) + return render_to_response('imglist.html', {'img_list': img_list}) + +def runloglist(request, url): + runlog_list = get_list_or_404(RunLogInfo) + return render_to_response('runloglist.html', {'runlog_list': runlog_list}) + +def render_methodlist(imagetype): + activetype, created = ImageType.objects.get_or_create(name=imagetype) + activetype.save() + active_list = ProcessingMethod.objects.filter(imagetype__name=imagetype) + method_list = get_list_or_404(ProcessingMethod) + return render_to_response('methodlist.html', {'method_list': method_list, 'active_list': active_list, 'activetype': activetype}) + +def methodlist(request): + return render_methodlist(request.GET['txttype']) + +def addmethod(request): + activetype = ImageType.objects.get(name=request.GET['activetype']) + selectedmethod = ProcessingMethod.objects.get(modulename = request.GET['modulename'], name=request.GET['methodname']) + activetype.methods.add(selectedmethod) + + return render_methodlist(request.GET['activetype']) + +def removemethod(request): + activetype = ImageType.objects.get(name=request.GET['activetype']) + selectedmethod = ProcessingMethod.objects.get(modulename = request.GET['modulename'], name=request.GET['methodname']) + activetype.methods.remove(selectedmethod) + + return render_methodlist(request.GET['activetype']) + +def editparams(request): + arg_list = MethodArgument.objects.filter(method__modulename=request.GET['modulename'], method__name=request.GET['methodname']) + #arg_list = MethodArgument.objects.filter(method__modulename=request.GET['modulename'], method__name=request.GET['methodname']).exclude(isROI=True) + #argROI_list = MethodArgument.objects.filter(method__modulename=request.GET['modulename'], method__name=request.GET['methodname']).filter(isROI=True) + + activemethod = ProcessingMethod.objects.get(modulename = request.GET['modulename'], name=request.GET['methodname']) + activetype = ImageType.objects.get(name=request.GET['activetype']) + + param_list = TypeParameters.objects.filter(imagetype__name=request.GET['activetype'], methodargument__method__modulename=request.GET['modulename'], methodargument__method__name=request.GET['methodname']) + ROI_list = TypeROI.objects.filter(imagetype__name=request.GET['activetype'], methodargument__method__modulename=request.GET['modulename'], methodargument__method__name=request.GET['methodname']) + + sampleframe = activetype.sample + + arg_params = {} + arg_rois ={} + + for arg in arg_list: + if arg.isROI: + if sampleframe: + ROI=ROI_list.filter(methodargument=arg) + if ROI: + arg_rois[arg.name]=json.dumps({"x1": ROI[0].x1, "x2": ROI[0].x2, "y1": ROI[0].y1, "y2": ROI[0].y2}) + else: + arg_rois[arg.name]='' + else: + param=param_list.filter(methodargument=arg) + if param: + arg_params[arg.name]=param[0].value + else: + arg_params[arg.name]='' + return render_to_response('paramlist.html', {'arg_list': arg_list, 'arg_params': arg_params, 'activemethod': activemethod, 'arg_rois': arg_rois, 'sampleframe': sampleframe}) + +def updateparams(request): + arg_list = MethodArgument.objects.filter(method__modulename=request.GET['activemodulename'], method__name=request.GET['activemethodname']) + activetype = ImageType.objects.get(name=request.GET['activetype']) + activemethod = ProcessingMethod.objects.get(modulename = request.GET['activemodulename'], name=request.GET['activemethodname']) + + roi_dict = json.loads(request.GET['rois']) + + for arg in arg_list: + if arg.name in request.GET: + if request.GET[arg.name] != '': + try: + param = TypeParameters.objects.get(imagetype = activetype, methodargument=arg) + param.value=request.GET[arg.name] + except TypeParameters.DoesNotExist: + param = TypeParameters.objects.create(imagetype = activetype, methodargument=arg, value=request.GET[arg.name]) + param.save() + if arg.name in roi_dict: + try: + roi = TypeROI.objects.get(imagetype = activetype, methodargument=arg) + except TypeROI.DoesNotExist: + roi = TypeROI.objects.create(imagetype = activetype, methodargument=arg) + + roi.x1 = roi_dict[arg.name][0] + roi.y1 = roi_dict[arg.name][1] + roi.x2 = roi_dict[arg.name][2] + roi.y2 = roi_dict[arg.name][3] + roi.save() + return HttpResponse("Success.") + +def processtype(request): + activetype = ImageType.objects.get(name=request.GET['activetype']) + activetype.ClearProcessed() + activetype.ProcessType() + return HttpResponse("Success.") + +def isnumber(s): + try: + float(s) + return True + except ValueError: + return False + +def filteredlist(request, url): + q=RunLogInfo.objects.all() + + if request.GET['name'] != "": + q=q.filter(sequencepath__contains=request.GET['name']) + if request.GET['description'] != "": + q=q.filter(description__contains=request.GET['description']) + if request.GET['starttime'] != "": + q=q.filter(time__gt=datetime.datetime.strptime(request.GET['starttime'],"%m/%d/%Y %H:%M:%S")) + if request.GET['endtime'] != "": + q=q.filter(time__lt=datetime.datetime.strptime(request.GET['endtime'],"%m/%d/%Y %H:%M:%S")) + if request.GET['varname'] != "": + if request.GET['varcomp'] == "ex": + q=q.filter(variablevalue__name__contains=request.GET['varname']) + elif isnumber(request.GET['varvalue']): + if request.GET['varcomp'] == "lt": + q=q.filter(variablevalue__name__contains=request.GET['varname'], variablevalue__value__lt=float(request.GET['varvalue'])) + elif request.GET['varcomp'] == "eq": + q=q.filter(variablevalue__name__contains=request.GET['varname'], variablevalue__value__eq=float(request.GET['varvalue'])) + elif request.GET['varcomp'] == "gt": + q=q.filter(variablevalue__name__contains=request.GET['varname'], variablevalue__value__gt=float(request.GET['varvalue'])) + return render_to_response('runloglist.html', {'runlog_list': q}) + +def countsample(request): + imgcount=ImageInfo.objects.filter(imgtype__name=request.GET['txttype']).count() + return HttpResponse(str(imgcount)) + +def getsample(request): + samplecount=int(request.GET['samplecount']) + sampleimage=ImageInfo.objects.filter(imgtype__name=request.GET['activetype'])[samplecount-1] + return render_to_response('sampleimages.html', {'sampleimage': sampleimage}) + +def setsample(request): + activetype = ImageType.objects.get(name=request.GET['activetype']) + src = RawFrame.objects.get(pngurl=request.GET['src']) + activetype.sample = src + activetype.save() + return HttpResponse("Success.") + + + + + + + + + + + + + + + + diff --git a/htroot/filter.htm b/htroot/filter.htm new file mode 100644 index 0000000..214a6c7 --- /dev/null +++ b/htroot/filter.htm @@ -0,0 +1,54 @@ + <html> + <head> + <link type="text/css" href="css/ui-lightness/jquery-ui-1.8.14.custom.css" rel="stylesheet" /> + <script type="text/javascript" src="./js/jquery.js"></script> + <script type="text/javascript" src="./js/jquery-ui.js"></script> + <script type="text/javascript" src="./js/jquery-ui-timepicker.js"></script> + <script type="text/javascript"> +$(function() { + $("#starttime" ).datetimepicker(); + $('#endtime').datetimepicker(); +}); + +$(document).ready(function() { + $('#filters').submit(function(event) { + event.preventDefault(); + + var values = $('form').serialize(); + + $.get("http://bec2.mit.edu/django/clinamen/filters/filter", values, function(data){ + $('#results').html(data); + }); + + //alert(values); + //$('#results').load('test.html'); + }); +}); + + +</script> + </head> + <body> + +<div> +<form id="filters"> +<p>Start Time: <input name="starttime" id="starttime" type="text"></p> +<p>End Time: <input name="endtime" id="endtime" type="text"></p> +<p>Sequence Name Contains: <input name="name" id="name" type="text"></p> +<p>Description Contains: <input name="description" id="description" type="text"></p> +<p>Variable Name Contains: <input name="varname" id="varname" type="text"> and + <select name="varcomp" id="varcomp"> + <option value="ex">exists</option> + <option value="gt">is greater than</option> + <option value="eq">is equal to</option> + <option value="lt">is less than</option> + </select> +<input name="varvalue" id="varvalue" type="text"> +</p> +<p><input type="submit" value="Search"><input type="reset"></p> +</form> +</div> +<hr> +<div id="results"><p></p></div> +</body> +</html> \ No newline at end of file diff --git a/htroot/processing.htm b/htroot/processing.htm new file mode 100644 index 0000000..0e02d6f --- /dev/null +++ b/htroot/processing.htm @@ -0,0 +1,221 @@ +<html> +<head> +<link type="text/css" href="css/ui-lightness/jquery-ui-1.8.14.custom.css" rel="stylesheet" /> +<link rel="stylesheet" type="text/css" href="css/imgareaselect-default.css" /> +<script type="text/javascript" src="./js/jquery.js"></script> +<script type="text/javascript" src="./js/jquery-ui.js"></script> +<script type="text/javascript" src="./scripts/jquery.imgareaselect.pack.js"></script> +<script type="text/javascript"> + +$(document).ready(function(){ + window.rois = {}; + window.sample ={}; + window.sample.count = 1; + window.sample.countmin = 1; + window.sample.countmax = 10; + + $('#between').html(window.sample.count); + + $('#gettype').click(function(event){ + event.preventDefault(); + + var values = $('form').serialize(); + + var jqhr=$.get("../django/clinamen/imgproc/countsample", values, function(data){ + window.sample.countmax=parseInt(data); + }) + + var jqhr=$.get("../django/clinamen/imgproc/methodlist", values, function(data){ + $('#methods').html(data); + }) + .complete(function() { + loadmethods(); + updatesamples(); + }); + }); + + $('#prevsample').click(function(event){ + if (window.sample.count - 1 >= window.sample.countmin) + { + window.sample.count = window.sample.count - 1; + } + $('#between').html(window.sample.count); + updatesamples(); + }); + + $('#nextsample').click(function(event){ + if (window.sample.count +1 <= window.sample.countmax) + { + window.sample.count = window.sample.count + 1; + } + $('#between').html(window.sample.count); + updatesamples(); + }); +}); + +function updatesamples() +{ + var jqhr=$.get("../django/clinamen/imgproc/getsample.html", + {activetype: $('#activetype').attr('data-activetype'), samplecount: window.sample.count}, function(data){ + $('#sample').html(data); + }) + .complete(function() {loadsamples();}); +} + +function updaterois() +{ + $('$img[name|="roi"]').each(function(index) { + if (this.getAttribute('data-roi') != '') { + var this_roi = JSON.parse(this.getAttribute('data-roi')); + $(this).imgAreaSelect({handles: true, x1: this_roi.x1, y1: this_roi.y1, x2: this_roi.x2, y2: this_roi.y2, + onSelectEnd: function (img, selection) { + window['rois'][img.getAttribute('data-arg')] = new Array(selection.x1, selection.y1, selection.x2, selection.y2); + } + }); + } else { + $(this).imgAreaSelect({handles: true, + onSelectEnd: function (img, selection) { + window['rois'][img.getAttribute('data-arg')] = new Array(selection.x1, selection.y1, selection.x2, selection.y2); + } + }); + } + }); +} + +function loadmethods() +{ + $('#addmethod').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/add.html", + {activetype: $('#activetype').attr('data-activetype'), modulename:$('#methodlist option:selected').attr('data-modulename'), + methodname:$('#methodlist option:selected').attr('data-methodname')}, function(data){ + $('#methods').html(data); + }) + .complete(function() {loadmethods();}); + }); + + $('$button[name|="remove"]').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/remove.html", + {activetype: $('#activetype').attr('data-activetype'), modulename: this.getAttribute('data-modulename'), + methodname: this.getAttribute('data-methodname')}, function(data){ + $('#methods').html(data); + $('#parameters').html(""); + }) + .complete(function() {loadmethods();}); + }); + + $('$button[name|="edit"]').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/edit.html", + {activetype: $('#activetype').attr('data-activetype'), modulename: this.getAttribute('data-modulename'), + methodname: this.getAttribute('data-methodname')}, function(data){ + $('#parameters').html(data); + }) + .complete(function() { + loadparams(); + }); + }); + + $('#processtype').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/process.html", + {activetype: $('#activetype').attr('data-activetype')}, function(data){}) + .complete(function() {}); + }); + +} + +function loadparams() +{ + $('#update').click(function(event) { + var values = $('form').serialize(); + values = values + '&' + $.param({activetype: $('#activetype').attr('data-activetype'), + activemodulename:$('#activemethod').attr('data-modulename'), activemethodname:$('#activemethod').attr('data-methodname')}) + + '&rois=' + JSON.stringify(window['rois']); + var jqhr=$.get("../django/clinamen/imgproc/update.html", values, function(data){ + }) + .complete(function() {}); + }); + + updaterois(); +} + +function loadsamples() +{ + $('$img[name|="sample"]').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/setsample.html", + {activetype: $('#activetype').attr('data-activetype'), src: this.getAttribute('src')}, function(data){}) + .complete(function() {}); + }); +} + +function loaditems() +{ + $('#addmethod').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/add.html", + {activetype: $('#activetype').attr('data-activetype'), modulename:$('#methodlist option:selected').attr('data-modulename'), + methodname:$('#methodlist option:selected').attr('data-methodname')}, function(data){ + $('#methods').html(data); + }) + .complete(function() {loaditems();}); + }); + + $('#update').click(function(event) { + var values = $('form').serialize(); + values = values + '&' + $.param({activetype: $('#activetype').attr('data-activetype'), + activemodulename:$('#activemethod').attr('data-modulename'), activemethodname:$('#activemethod').attr('data-methodname')}) + + '&rois=' + JSON.stringify(window['rois']); + var jqhr=$.get("../django/clinamen/imgproc/update.html", values, function(data){ + }) + .complete(function() {loaditems();}); + }); + + $('$button[name|="remove"]').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/remove.html", + {activetype: $('#activetype').attr('data-activetype'), modulename: this.getAttribute('data-modulename'), + methodname: this.getAttribute('data-methodname')}, function(data){ + $('#methods').html(data); + $('#parameters').html(""); + }) + .complete(function() {loaditems();}); + }); + + $('$button[name|="edit"]').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/edit.html", + {activetype: $('#activetype').attr('data-activetype'), modulename: this.getAttribute('data-modulename'), + methodname: this.getAttribute('data-methodname')}, function(data){ + $('#parameters').html(data); + }) + .complete(function() { + loaditems(); + updaterois(); + }); + }); + + $('$img[name|="sample"]').click(function(event) { + var jqhr=$.get("../django/clinamen/imgproc/setsample.html", + {activetype: $('#activetype').attr('data-activetype'), src: this.getAttribute('src')}, function(data){}) + .complete(function() {loaditems();}); + }); +} +</script> +</head> +<body> + +<div> +<form id="imagetypeform"> + <p><b>Image Type:</b> <input name="txttype" id="txttype" type="text"></p> + <p><input type="button" name="gettype" id="gettype" value="Find Type"></p> + + <hr> + <div id="methods"><p></p></div> + + <hr> + <div id="parameters"><p></p></div> + + <hr> + <div id="sampleform"> </div> + <input type="button" id="prevsample" value="<"><span id='between'></span><input type="button" id="nextsample" value=">"> + + <div id="sample"></div> + +</form> +</body> +</html> \ No newline at end of file diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..ad1c082 --- /dev/null +++ b/settings.py @@ -0,0 +1,98 @@ +# Django settings for clinamen project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@domain.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': 'filelist', # Or path to database file if using sqlite3. + 'USER': 'root', # Not used with sqlite3. + 'PASSWORD': 'w0lfg4ng', # Not used with sqlite3. + 'HOST': 'localhost', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '', # Set to empty string for default. Not used with sqlite3. + } +} + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Chicago' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = './static/' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = 'static/' + +# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a +# trailing slash. +# Examples: "http://foo.com/media/", "/media/". +ADMIN_MEDIA_PREFIX = '/media/' + +# Make this unique, and don't share it with anybody. +SECRET_KEY = '81_-&kt2=s7*u!xs40vgxp%rv^7k=*a(z23g5a6w)%n0%_-sg^' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +) + +ROOT_URLCONF = 'clinamen.urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + "C:/django/django/clinamen/templates" +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'clinamen.filelist', + # Uncomment the next line to enable the admin: + # 'django.contrib.admin', + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', +) diff --git a/templates/imglist.html b/templates/imglist.html new file mode 100644 index 0000000..689ca2d --- /dev/null +++ b/templates/imglist.html @@ -0,0 +1,24 @@ +<table border=1> +<tr> +<th>Image time +<th>Processed frames +<th>Raw frames +</tr> +{% for imginfo in img_list %} +<tr> +<td>{{imginfo.time|date:"M-d-Y H:i:s"}} +<td> +{% for thumbinfo in imginfo.thumbinfo_set.all %} +{% if thumbinfo.framenumber < 0 %} +<img src="{{thumbinfo.url}}" height=64 width=64> +{% endif %} +{% endfor %} +<td> +{% for thumbinfo in imginfo.thumbinfo_set.all %} +{% if thumbinfo.framenumber >= 0 %} +<img src="{{thumbinfo.url}}" height=64 width=64> +{% endif %} +{% endfor %} +</tr> +{% endfor %} +</table> \ No newline at end of file diff --git a/templates/methodlist.html b/templates/methodlist.html new file mode 100644 index 0000000..ba0907e --- /dev/null +++ b/templates/methodlist.html @@ -0,0 +1,18 @@ +<div id="activetype" data-activetype={{activetype.name}}><b>Image type:</b> {{activetype.name}}</div> + +<p>Add a processing method: +<select id="methodlist"> + {% for method in method_list %} + <option data-modulename="{{method.modulename}}" data-methodname="{{method.name}}" value="{{method.modulename}}___{{method.name}}">{{method.modulename}}::{{method.name}}</option> + {% endfor %} +</select> +</p> +<p><input type="button" name="addmethod" id="addmethod" value="Add this method"></p> + +<p>Active methods:</p> +{% for method in active_list %} +<p><b>Module name:</b> {{method.modulename}} <b>Function name:</b> {{method.name}} <input type="button" name="edit" data-modulename="{{method.modulename}}" data-methodname="{{method.name}}" value="Edit Parameters"><input type="button" name="remove" data-modulename="{{method.modulename}}" data-methodname="{{method.name}}" value="Remove method"></p> +{% endfor %} + +<p><input type="button" name="processtype" id="processtype" value="Process this type"></p> + diff --git a/templates/paramlist.html b/templates/paramlist.html new file mode 100644 index 0000000..5389631 --- /dev/null +++ b/templates/paramlist.html @@ -0,0 +1,15 @@ +<div id="activemethod" data-modulename={{activemethod.modulename}} data-methodname={{activemethod.name}}><b>Parameters for method:</b> {{activemethod.modulename}}.{{activemethod.name}}</div> + +{% for arg, value in arg_params.items %} +<p>{{arg}}: <input type="text" name={{arg}} value={{value}}> +{% endfor %} + +{% for arg, value in arg_rois.items %} +<p>{{arg}}: <img name="roi" data-arg="{{arg}}" data-roi="{{value}}" src={{sampleframe.pngurl}}> +{% endfor %} + +<p><input type="button" id="update" value="Update Values" name="update"></p> + + + + diff --git a/templates/runloglist.html b/templates/runloglist.html new file mode 100644 index 0000000..33d7469 --- /dev/null +++ b/templates/runloglist.html @@ -0,0 +1,47 @@ +<table border=1> +<tr> +<th>Run time +<th>Sequence path +<th>Duration +<th>Description +<th>Variables +<th>Image time +<th>Image type +<th>Raw frames +<th>Processed frames +</tr> +{% for runloginfo in runlog_list %} +<tr> +<td>{{runloginfo.time|date:"M-d-Y H:i:s"}} +<td>{{runloginfo.sequencepath}} +<td>{{runloginfo.sequenceduration}} +<td>{{runloginfo.description}} +<td> +{% for varvalue in runloginfo.variablevalue_set.all %} +{{varvalue.name}} = {{varvalue.value}} <BR> +{% endfor %} +<td> +{% for imginfo in runloginfo.imageinfo_set.all|slice:":2" %} +{{imginfo.time}}<BR> +{% endfor %} +<td> +{% for imginfo in runloginfo.imageinfo_set.all|slice:":2" %} +{{imginfo.imgtype.name}}<BR> +{% endfor %} +<td> +{% for imginfo in runloginfo.imageinfo_set.all|slice:":2" %} +{% for frameinfo in imginfo.rawframe_set.all %} +<a href="{{frameinfo.pngurl}}"><img src="{{frameinfo.thumburl}}" height=64 width=64></a> +{% endfor %} +<BR> +{% endfor %} +<td> +{% for imginfo in runloginfo.imageinfo_set.all|slice:":2" %} +{% for frameinfo in imginfo.processedframe_set.all %} +<a href="{{frameinfo.pngurl}}"><img src="{{frameinfo.thumburl}}" height=64 width=64></a> +{% endfor %} +<BR> +{% endfor %} +</tr> +{% endfor %} +</table> \ No newline at end of file diff --git a/templates/sampleimages.html b/templates/sampleimages.html new file mode 100644 index 0000000..3d4d9a2 --- /dev/null +++ b/templates/sampleimages.html @@ -0,0 +1,3 @@ +{% for frameinfo in sampleimage.rawframe_set.all %} +<img name="sample" src="{{frameinfo.pngurl}}"> +{% endfor %} \ No newline at end of file diff --git a/urls.py b/urls.py new file mode 100644 index 0000000..7c42b64 --- /dev/null +++ b/urls.py @@ -0,0 +1,31 @@ +from django.conf.urls.defaults import * + +# Uncomment the next two lines to enable the admin: +# from django.contrib import admin +# admin.autodiscover() + +urlpatterns = patterns('', + (r'^clinamen/images/([A-Za-z]+)', 'clinamen.filelist.views.imagelist'), + (r'^clinamen/runlogs/([A-Za-z]+)', 'clinamen.filelist.views.runloglist'), + (r'^clinamen/filters/([A-Za-z]+)', 'clinamen.filelist.views.filteredlist'), + # Example: + # (r'^clinamen/', include('clinamen.foo.urls')), + + (r'^clinamen/imgproc/methodlist', 'clinamen.filelist.views.methodlist'), + (r'^clinamen/imgproc/remove', 'clinamen.filelist.views.removemethod'), + (r'^clinamen/imgproc/add', 'clinamen.filelist.views.addmethod'), + (r'^clinamen/imgproc/edit', 'clinamen.filelist.views.editparams'), + (r'^clinamen/imgproc/update', 'clinamen.filelist.views.updateparams'), + (r'^clinamen/imgproc/process', 'clinamen.filelist.views.processtype'), + + (r'^clinamen/imgproc/countsample', 'clinamen.filelist.views.countsample'), + (r'^clinamen/imgproc/getsample', 'clinamen.filelist.views.getsample'), + (r'^clinamen/imgproc/setsample', 'clinamen.filelist.views.setsample'), + + # Uncomment the admin/doc line below to enable admin documentation: + # (r'^admin/doc/', include('django.contrib.admindocs.urls')), + + # Uncomment the next line to enable the admin: + # (r'^admin/', include(admin.site.urls)), + (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': 'static'}), +) \ No newline at end of file