def order_status(request, task_id): import urllib2, json, os from celery.result import AsyncResult task = AsyncResult(task_id) #u = urllib2.urlopen('http://%s:%s/api/task/info/%s' % (os.environ['VIP_FLOWER_HOST'], # os.environ['VIP_FLOWER_PORT'], # task_id)) #status = json.loads(u.read()); #status['task_id'] = status['task-id'] #jinja2 limitation status = { 'task': task } if task.state == 'PROCESSING' and task.result[ 'stage'] == 'generate match points': from vsi.iglob import glob status['mat'] = len( glob(os.path.join(task.result['processing_dir'], '*.mat'), False)) status['sift'] = len( glob(os.path.join(task.result['processing_dir'], '*.sift'), False)) return render(request, 'order/visualsfm/html/order_status.html', status)
def order_status(request, task_id): import urllib2, json, os from celery.result import AsyncResult task = AsyncResult(task_id) status = {'task': task} if task.state == 'PROCESSING' and task.result[ 'stage'] == 'generate match points': from vsi.iglob import glob status['mat'] = len( glob(os.path.join(task.result['processing_dir'], '*.mat'), False)) status['sift'] = len( glob(os.path.join(task.result['processing_dir'], '*.sift'), False)) return render(request, 'visualsfm/html/order_status.html', status)
def order_status(request, task_id): import urllib2, json, os from celery.result import AsyncResult task = AsyncResult(task_id); #u = urllib2.urlopen('http://%s:%s/api/task/info/%s' % (os.environ['VIP_FLOWER_HOST'], # os.environ['VIP_FLOWER_PORT'], # task_id)) #status = json.loads(u.read()); #status['task_id'] = status['task-id'] #jinja2 limitation status = {'task': task}; if task.state == 'PROCESSING' and task.result['stage'] == 'generate match points': from vsi.iglob import glob status['mat'] = len(glob(os.path.join(task.result['processing_dir'], '*.mat'), False)) status['sift'] = len(glob(os.path.join(task.result['processing_dir'], '*.sift'), False)) return render(request, 'order/visualsfm/html/order_status.html', status)
def run(self): import csv from django.contrib.gis import geos from vsi.iglob import glob from voxel_globe.meta.models import ControlPoint csv_files = glob(os.path.join(self.ingest_dir, '*.csv'), False) for csv_file in csv_files: with open(csv_file, 'r') as fid: reader = csv.reader(fid, delimiter=',') for line in reader: try: point = geos.Point(float(line[2]),float(line[3]), float(line[4]), srid=int(line[1])) ControlPoint.create(name=line[0], description="Ingested point", point=point, apparentPoint=point, service_id=self.task.request.id).save() except: pass
def ingest_data(self, uploadSession_id, imageDir): ''' task for the ingest route, to ingest the data an upload sessions points to ''' import voxel_globe.ingest.models as IngestModels from .tools import loadAdjTaggedMetadata import numpy from voxel_globe.tools.camera import save_krt from PIL import Image uploadSession = IngestModels.UploadSession.objects.get(id=uploadSession_id); #directories = uploadSession.directory.all(); #imageDirectory = directories.filter(name='image') #metaDirectory = directories.filter(name='meta') metadataFilename = glob(os.path.join(imageDir, '*', '*_adj_tagged_images.txt'), False); if not len(metadataFilename) == 1: logger.error('Only one metadatafile should have been found, found %d instead', len(metadataFilename)); try: metadataFilename = metadataFilename[0] (day, timeOfDay) = os.path.split(metadataFilename)[1].split(' '); timeOfDay = timeOfDay.split('_', 1)[0]; except: metadataFilename = os.devnull; day = 'NYA' timeOfDay = 'NYA' imageCollection = voxel_globe.meta.models.ImageCollection.create(name="Arducopter Upload %s %s %s (%s)" % (uploadSession.name, day, timeOfDay, uploadSession_id), service_id = self.request.id); imageCollection.save(); for d in glob(os.path.join(imageDir, '*'+os.path.sep), False): files = glob(os.path.join(d, '*.jpg'), False); files.sort() for f in files: self.update_state(state='PROCESSING', meta={'stage':'File %s of %d' % (f, len(files))}) logger.debug('Processing %s of %s', f, len(files)) zoomifyName = f[:-4] + '_zoomify' pid = Popen(['vips', 'dzsave', f, zoomifyName, '--layout', 'zoomify']) pid.wait(); #convert the slashes to URL slashes relFilePath = urllib.pathname2url(os.path.relpath(f, env['VIP_IMAGE_SERVER_ROOT'])); basename = os.path.split(f)[-1] relZoomPath = urllib.pathname2url(os.path.relpath(zoomifyName, env['VIP_IMAGE_SERVER_ROOT'])); image = Image.open(f) if image.bits == 8: pixel_format = 'b'; if image.bits == 16: pixel_format = 's'; if image.bits == 32: if image.mode == "I": pixel_format = 'i'; elif image.mode == "F": pixel_format = 'f' img = voxel_globe.meta.models.Image.create( name="Arducopter Upload %s (%s) Frame %s" % (uploadSession.name, uploadSession_id, basename), imageWidth=image.size[0], imageHeight=image.size[1], numberColorBands=image.layers, pixelFormat=pixel_format, fileFormat='zoom', imageUrl='%s://%s:%s/%s/%s/' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], relZoomPath), originalImageUrl='%s://%s:%s/%s/%s' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], relFilePath), service_id = self.request.id); img.save(); imageCollection.images.add(img); self.update_state(state='Processing', meta={'stage':'metadata'}) metadata = loadAdjTaggedMetadata(metadataFilename); for meta in metadata: try: img = imageCollection.images.get(name__icontains='Frame %s'%meta.filename) k = numpy.eye(3); k[0,2] = img.imageWidth/2; k[1,2] = img.imageHeight/2; r = numpy.eye(3); t = [0, 0, 0]; origin = meta.llh_xyz; save_krt(self.request.id, img, k, r, t, origin, srid=7428); except Exception as e: logger.warning('%s', e) logger.error('Could not match metadata entry for %s' % meta.filename) averageGps = numpy.mean(numpy.array(map(lambda x:x.llh_xyz, metadata)), 0); voxel_globe.meta.models.Scene.create(name="Arducopter origin %s (%s)" % (uploadSession.name, uploadSession_id), service_id = self.request.id, origin='SRID=%d;POINT(%0.12f %0.12f %0.12f)' % \ (7428, averageGps[0], averageGps[1], averageGps[2])).save() uploadSession.delete()
def add_arducopter_images(self, *args, **kwargs): images = glob(path_join(env['VIP_PROJECT_ROOT'], 'images', '1fps*', ''), False); images.sort(); imageCollection = []; for image in images: image = os.path.basename(os.path.dirname(image)); frameNum = image[11:15] if voxel_globe.meta.models.Image.objects.filter(name="Arducopter Mission 2 Frame:%s" % frameNum): raise Exception('Already exists'); img = voxel_globe.meta.models.Image.create(name="Arducopter Mission 2 Frame:%s" % frameNum, imageWidth=4096, imageHeight=2160, numberColorBands=3, pixelFormat='b', fileFormat='zoom', imageUrl='%s://%s:%s/%s/%s/' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], image), originalImageUrl='%s://%s:%s/%s/%s.jpg' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], image), service_id = self.request.id); img.save(); imageCollection.append(img.id); ic = voxel_globe.meta.models.ImageCollection.create(name="Arducopter Mission 2", service_id = self.request.id); ic.save(); ic.images.add(*imageCollection); ic = voxel_globe.meta.models.ImageCollection.create(name="Arducopter Mission 2 short", service_id = self.request.id); ic.save(); ic.images.add(*imageCollection[101:151]); with open(path_join(env['VIP_PROJECT_ROOT'], 'images', 'Contractor_Survey_NorthA_List.csv'), 'r') as fid: lines = fid.readlines(); lines = map(lambda x: x.split(','), lines); for line in lines[3:]: name = line[1]; desc = line[2]; lat = float(line[3]) + float(line[4])/60.0 + float(line[5])/3600.0; if line[6] == 'S': lat = -lat; lon = float(line[8]) + float(line[9])/60.0 + float(line[10])/3600.0; if line[11] == 'W': lon = -lon; alt = float(line[13]); point = geos.Point(lon, lat, alt) tp = voxel_globe.meta.models.ControlPoint.create(name=name, description=desc, point=point, apparentPoint=point) tp.service_id = self.request.id; tp.save(); print '********** Populating arducopter cameras **********' add_sample_cameras(self, path_join(env['VIP_PROJECT_ROOT'], 'images', 'cannon_cameras_gps.txt'), srid=7428) voxel_globe.meta.models.Scene.create(name="Arducopter Mission 2 origin", service_id = self.request.id, origin='SRID=%d;POINT(%0.12f %0.12f %0.12f)' % (7428, -92.215197, 37.648858, 300)).save()
def run_test(self, pattern, result, resultI): self.assertEqual(len(glob(pattern, True)), result) self.assertEqual(len(glob(pattern, False)), resultI) self.assertItemsEqual(glob(pattern), glob_orig(pattern))
def ingest_data(self, uploadSession_id, imageDir): ''' task for the ingest route, to ingest the data an upload sessions points to ''' import voxel_globe.ingest.models as IngestModels from voxel_globe.tools.camera import save_krt from PIL import Image from datetime import datetime, timedelta from .tools import split_clif uploadSession = IngestModels.UploadSession.objects.get(id=uploadSession_id); metadataFilenames = glob(os.path.join(imageDir, '*.txt'), False); metadataFilenames = sorted(metadataFilenames, key=lambda s:s.lower()) metadataBasenames = map(lambda x:os.path.basename(x).lower(), metadataFilenames) #In case none of them succeeded... date = 'NYA' timeOfDay = 'NYA' for metadata_filename in metadataFilenames: #Loop through until one succeeds try: with open(metadata_filename, 'r') as fid: data = fid.readline().split(',') imu_time = float(data[6]) imu_week = int(data[7]) timestamp = datetime(1980, 1, 6) + timedelta(weeks=imu_week, seconds=imu_time) date = '%04d-%02d-%02d' % (timestamp.year, timestamp.month, timestamp.day) timeOfDay = '%02d:%02d:%02d.%06d' % (timestamp.hour, timestamp.minute, timestamp.second, timestamp.microsecond) break #Break on first success except: pass imageCollection = voxel_globe.meta.models.ImageCollection.create( name="CLIF Upload %s %s %s (%s)" % (uploadSession.name, date, timeOfDay, uploadSession_id), service_id = self.request.id); imageCollection.save(); llhs_xyz = [] #for d in glob(os.path.join(imageDir, '*'+os.path.sep), False): if 1: files = glob(os.path.join(imageDir, '*'+os.extsep+'raw'), False); files.sort() for index,f in enumerate(files): self.update_state(state='PROCESSING', meta={'stage':'File %s (%d of %d)' % (f, index+1, len(files))}) logger.debug('Processing %s (%d of %d)', f, index+1, len(files)) basename = os.path.basename(f) img_filename = os.extsep.join([os.path.splitext(f)[0], 'png']) with open(f, 'rb') as fid: data = fid.read(); img = np.fromstring(data, dtype=CLIF_DATA[CLIF_VERSION]['dtype']).reshape( (CLIF_DATA[CLIF_VERSION]['width'], CLIF_DATA[CLIF_VERSION]['height'])).T img2 = Image.fromarray(img) img2.save(img_filename) zoomifyName = os.path.splitext(f)[0] + '_zoomify' pid = Popen(['vips', 'dzsave', img_filename, zoomifyName, '--layout', 'zoomify']) pid.wait(); #convert the slashes to URL slashes relFilePath = urllib.pathname2url(os.path.relpath(img_filename, env['VIP_IMAGE_SERVER_ROOT'])); basename = os.path.split(f)[-1] relZoomPath = urllib.pathname2url(os.path.relpath(zoomifyName, env['VIP_IMAGE_SERVER_ROOT'])); pixel_format = CLIF_DATA[CLIF_VERSION]['pixel_format'] width = CLIF_DATA[CLIF_VERSION]['width'] height = CLIF_DATA[CLIF_VERSION]['height'] bands = CLIF_DATA[CLIF_VERSION]['bands'] img = voxel_globe.meta.models.Image.create( name="CLIF Upload %s (%s) Frame %s" % (uploadSession.name, uploadSession_id, basename), imageWidth=width, imageHeight=height, numberColorBands=bands, pixelFormat=pixel_format, fileFormat='zoom', imageUrl='%s://%s:%s/%s/%s/' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], relZoomPath), originalImageUrl='%s://%s:%s/%s/%s' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], relFilePath), service_id = self.request.id); img.save(); imageCollection.images.add(img); metadata_filename_desired = split_clif(f) metadata_filename_desired = '%06d-%s.txt' % (0, metadata_filename_desired[2]) if 1: # try: metadata_index = metadataBasenames.index(metadata_filename_desired) metadata_filename = metadataFilenames[metadata_index] with open(metadata_filename, 'r') as fid: metadata = fid.readline().split(',') llh_xyz = [float(metadata[4]), float(metadata[3]), float(metadata[5])*CLIF_DATA[CLIF_VERSION]['altitude_conversion']] llhs_xyz.append(llh_xyz) k = np.eye(3); k[0,2] = width/2; k[1,2] = height/2; r = np.eye(3); t = [0, 0, 0]; origin = llh_xyz; save_krt(self.request.id, img, k, r, t, origin, srid=4326); # except Exception as e: pass averageGps = np.mean(np.array(llhs_xyz), 0); voxel_globe.meta.models.Scene.create(name="CLIF origin %s (%s)" % (uploadSession.name, uploadSession_id), service_id = self.request.id, origin='SRID=%d;POINT(%0.12f %0.12f %0.12f)' % \ (4326, averageGps[0], averageGps[1], averageGps[2])).save() uploadSession.delete()
def runVisualSfm(self, imageSetId, sceneId, cleanup=True): from voxel_globe.meta import models from os import environ as env from os.path import join as path_join import os import shutil import time from django.contrib.gis.geos import Point from .tools import writeNvm, writeGcpFile, generateMatchPoints, runSparse,\ readNvm import voxel_globe.tools from voxel_globe.tools.camera import get_kto, save_krt import voxel_globe.tools.enu as enu import numpy import boxm2_adaptor import boxm2_scene_adaptor from voxel_globe.tools.xml_dict import load_xml from django.contrib.gis.geos import Point from voxel_globe.tools.image import convert_image from distutils.spawn import find_executable from vsi.iglob import glob as glob self.update_state(state='INITIALIZE', meta={'stage': 0}) #Make main temp dir and cd into it with voxel_globe.tools.task_dir('visualsfm', cd=True) as processing_dir: #Because visualsfm is so... bad, I need to copy it locally so I can #configure it visualsfm_exe = os.path.join(processing_dir, 'visualsfm') shutil.copy(find_executable('VisualSFM'), visualsfm_exe) with open(os.path.join(processing_dir, 'nv.ini'), 'w') as fid: fid.write('param_search_multiple_models 0\n') fid.write('param_use_siftgpu 2\n') matchFilename = path_join(processing_dir, 'match.nvm') sparce_filename = path_join(processing_dir, 'sparse.nvm') #This can NOT be changed in version 0.5.25 gcpFilename = matchFilename + '.gcp' logger.debug('Task %s is processing in %s' % (self.request.id, processing_dir)) image_set = models.ImageSet.objects.get(id=imageSetId) imageList = image_set.images.all() ### if 1: ### try: #Not fully integrated yet ### sift_gpu = siftgpu.SiftGPU() ### except: ### pass localImageList = [] for x in range(len(imageList)): #Download the image locally image = imageList[x] self.update_state(state='INITIALIZE', meta={ 'stage': 'image fetch', 'i': x, 'total': len(imageList) }) imageName = image.filename_path extension = os.path.splitext(imageName)[1].lower() localName = path_join(processing_dir, 'frame_%05d%s' % (x + 1, extension)) #lncp(imageName, localName) #Stupid VisualSFM dereferences symlinks, breaking this shutil.copyfile(imageName, localName) #Convert the image if necessary if extension not in ['.jpg', '.jpeg', '.pgm', '.ppm']: self.update_state(state='INITIALIZE', meta={ 'stage': 'image convert', 'i': x, 'total': len(imageList) }) #Add code here to converty to jpg for visual sfm if extension in ['.png']: #'not implemented': from PIL import Image image_temp = Image.open(localName) # if len(image_temp.mode) > 1: #Stupid visual sfm is picky :( # new_local_name = os.path.splitext(localName)[0] + '.ppm' # else: # new_local_name = os.path.splitext(localName)[0] + '.pgm' new_local_name = os.path.splitext(localName)[0] + '.jpg' ###ingest.convert_image(localName, new_local_name, 'PNM') convert_image(localName, new_local_name, 'JPEG', options=('QUALITY=100', )) os.remove(localName) localName = new_local_name else: raise Exception('Unsupported file type') imageInfo = {'localName': localName, 'index': x} try: [K, T, llh] = get_kto(image) imageInfo['K_intrinsics'] = K imageInfo['transformation'] = T imageInfo['enu_origin'] = llh except: pass localImageList.append(imageInfo) ### if 1: ### try: #not fully integrated yet ### sift_gpu.create_sift(localName, os.path.splitext(localName)[0]+'.sift') ### except: ### pass # filenames = list(imageList.values_list('image_url')) # logger.info('The image list 0is %s' % filenames) self.update_state(state='PROCESSING', meta={ 'stage': 'generate match points', 'processing_dir': processing_dir, 'total': len(imageList) }) pid = generateMatchPoints(map(lambda x: x['localName'], localImageList), matchFilename, logger=logger, executable=visualsfm_exe) old_mat = None old_sift = None #TODO: Replace with inotify to monitor directory while pid.poll() is None: mat = len(glob(os.path.join(processing_dir, '*.mat'), False)) sift = len(glob(os.path.join(processing_dir, '*.sift'), False)) if mat != old_mat or \ sift != old_sift: old_mat = mat old_sift = sift self.update_state(state='PROCESSING', meta={ 'stage': 'generate match points', 'processing_dir': processing_dir, 'sift': sift, 'mat': mat, 'total': len(imageList) }) time.sleep(5) # cameras = [] # for image in imageList: # if 1: # #try: # [K, T, llh] = get_kto(image) # cameras.append({'image':image.id, 'K':K, 'tranformation': # T, 'origin':llh}) # #except: # pass # origin = numpy.median(origin, axis=0) # origin = [-92.215197, 37.648858, 268.599] scene = models.Scene.objects.get(id=sceneId) origin = list(scene.origin) if scene.geolocated: self.update_state(state='PROCESSING', meta={'stage': 'writing gcp points'}) #find the middle origin, and make it THE origin data = [] #.name .llh_xyz for imageInfo in localImageList: try: r = imageInfo['transformation'][0:3, 0:3] t = imageInfo['transformation'][0:3, 3:] enu_point = -r.transpose().dot(t) if not numpy.array_equal(imageInfo['enu_origin'], origin): ecef = enu.enu2xyz( refLong=imageInfo['enu_origin'][0], refLat=imageInfo['enu_origin'][1], refH=imageInfo['enu_origin'][2], #e=imageInfo['transformation'][0, 3], #n=imageInfo['transformation'][1, 3], #u=imageInfo['transformation'][2, 3]) e=enu_point[0], n=enu_point[1], u=enu_point[2]) enu_point = enu.xyz2enu(refLong=origin[0], refLat=origin[1], refH=origin[2], X=ecef[0], Y=ecef[1], Z=ecef[2]) # else: # enu_point = imageInfo['transformation'][0:3, 3] dataBit = { 'filename': imageInfo['localName'], 'xyz': enu_point } data.append(dataBit) #Make this a separate ingest process, making CAMERAS linked to the #images #data = arducopter.loadAdjTaggedMetadata( # r'd:\visualsfm\2014-03-20 13-22-44_adj_tagged_images.txt') #Make this read the cameras from the DB instead writeGcpFile(data, gcpFilename) except: #some images may have no camera pass self.update_state(state='PROCESSING', meta={'stage': 'sparse SFM'}) pid = runSparse(matchFilename, sparce_filename, gcp=scene.geolocated, shared=True, logger=logger, executable=visualsfm_exe) pid.wait() self.update_state(state='FINALIZE', meta={'stage': 'loading resulting cameras'}) #prevent bundle2scene from getting confused and crashing sift_data = os.path.join(processing_dir, 'sift_data') os.mkdir(sift_data) for filename in glob(os.path.join(processing_dir, '*.mat'), False) +\ glob(os.path.join(processing_dir, '*.sift'), False): shutil.move(filename, sift_data) if scene.geolocated: #Create a uscene.xml for the geolocated case. All I want out of this is #the bounding box and gsd calculation. boxm2_adaptor.bundle2scene(sparce_filename, processing_dir, isalign=False, out_dir="") cams = readNvm(path_join(processing_dir, 'sparse.nvm')) #cams.sort(key=lambda x:x.name) #Since the file names are frame_00001, etc... and you KNOW this order is #identical to localImageList, with some missing camera_set = models.CameraSet(name="Visual SFM Geo %s" % image_set.name, service_id=self.request.id, images_id=imageSetId) camera_set.save() for cam in cams: frameName = cam.name #frame_00001, etc.... imageInfo = filter( lambda x: x['localName'].endswith(frameName), localImageList)[0] #I have to use endswith instead of == because visual sfm APPARENTLY #decides to take some liberty and make absolute paths relative image = imageList[imageInfo['index']] (k, r, t) = cam.krt(width=image.image_width, height=image.image_height) t = t.flatten() camera = save_krt(self.request.id, image, k, r, t, origin, srid=4326) camera_set.cameras.add(camera) else: from vsi.tools.natural_sort import natural_sorted from vsi.io.krt import Krt boxm2_adaptor.bundle2scene(sparce_filename, processing_dir, isalign=True, out_dir=processing_dir) #While the output dir is used for the b2s folders, uscene.xml is cwd #They are both set to processing_dir, so everything works out well aligned_cams = glob(os.path.join(processing_dir, 'cams_krt', '*')) #sort them naturally in case there are more then 99,999 files aligned_cams = natural_sorted(aligned_cams) if len(aligned_cams) != len(imageList): #Create a new image set new_image_set = models.ImageSet(name="SFM Result Subset (%s)" % image_set.name, service_id=self.request.id) # for image in image_set.images.all(): # new_image_set.images.add(image) new_image_set.save() frames_keep = set( map( lambda x: int(os.path.splitext(x.split('_')[-2])[0]) - 1, aligned_cams)) for frame_index in frames_keep: new_image_set.images.add(imageList[frame_index]) # frames_remove = set(xrange(len(imageList))) - frames_keep # # for remove_index in list(frames_remove): # #The frame number refers to the nth image in the image set, # #so frame_00100.tif is the 100th image, starting the index at one # #See local_name above # # #remove the images sfm threw away # new_image_set.remove(imageList[remove_index]) image_set = new_image_set frames_keep = list(frames_keep) else: frames_keep = xrange(len(aligned_cams)) camera_set = models.CameraSet(name="Visual SFM %s" % image_set.name, service_id=self.request.id, images_id=imageSetId) camera_set.save() #---Update the camera models in the database.--- for camera_index, frame_index in enumerate(frames_keep): krt = Krt.load(aligned_cams[camera_index]) image = imageList[frame_index] camera = save_krt(self.request.id, image, krt.k, krt.r, krt.t, [0, 0, 0], srid=4326) camera_set.cameras.add(camera) #---Update scene information important for the no-metadata case --- scene_filename = os.path.join(processing_dir, 'model', 'uscene.xml') boxm_scene = boxm2_scene_adaptor.boxm2_scene_adaptor(scene_filename) scene.bbox_min = Point(*boxm_scene.bbox[0]) scene.bbox_max = Point(*boxm_scene.bbox[1]) #This is not a complete or good function really... but it will get me the #information I need. scene_dict = load_xml(scene_filename) block = scene_dict['block'] scene.default_voxel_size = Point(float(block.at['dim_x']), float(block.at['dim_y']), float(block.at['dim_z'])) scene.save()
def ingest_data(self, uploadSession_id, imageDir): ''' task for the ingest route, to ingest the data an upload sessions points to ''' import voxel_globe.ingest.models as IngestModels import numpy from voxel_globe.tools.camera import save_krt uploadSession = IngestModels.UploadSession.objects.get(id=uploadSession_id); #directories = uploadSession.directory.all(); #imageDirectory = directories.filter(name='image') #metaDirectory = directories.filter(name='meta') imageCollection = voxel_globe.meta.models.ImageCollection.create(name="Generic Upload %s (%s)" % (uploadSession.name, uploadSession_id), service_id = self.request.id); imageCollection.save(); r = numpy.eye(3); t = [0, 0, 0]; gpsList = [] gpsList2 = [] for d in glob(os.path.join(imageDir, '*'+os.path.sep), False): files = glob(os.path.join(d, '*'), False); files.sort() for f in files: self.update_state(state='PROCESSING', meta={'stage':'File %s of %d' % (f, len(files))}) zoomifyName = f[:-4] + '_zoomify' pid = Popen(['vips', 'dzsave', f, zoomifyName, '--layout', 'zoomify']) pid.wait(); #convert the slashes to URL slashes relFilePath = urllib.pathname2url(os.path.relpath(f, env['VIP_IMAGE_SERVER_ROOT'])); basename = os.path.split(f)[-1] relZoomPath = urllib.pathname2url(os.path.relpath(zoomifyName, env['VIP_IMAGE_SERVER_ROOT'])); with open(f, 'rb') as fid: magic = fid.read(4) image_info = {} if magic == '49492A00'.decode('hex') or \ magic == '4D4D002A'.decode('hex'): logger.debug('Tifffile: %s', f) from tifffile import TiffFile with TiffFile(f) as image: if image.pages[0].dtype == 's': image_info['dtype'] = numpy.dtype('S') else: image_info['dtype'] = numpy.dtype(image.pages[0].dtype) image_info['bps'] = image.pages[0].bits_per_sample image_info['height'] = image.pages[0].shape[0] #Yep, y,x,z order image_info['width'] = image.pages[0].shape[1] try: image_info['bands'] = image.pages[0].shape[2] except IndexError: image_info['bands'] = 1 else: logger.debug('Pil: %s', f) from PIL import Image with Image.open(f) as image: #The getmode* commands do not give you the REAL datatypes. I need the #REAL (numpy in this case) bps, not some random PIL designation image_info['dtype'] = numpy.dtype(Image._MODE_CONV[image.mode][0]) #probably doesn't work well for bool... Oh well image_info['bps'] = image_info['dtype'].itemsize*8 image_info['width'] = image.size[0] #Yep, x,y order image_info['height'] = image.size[1] image_info['bands'] = Image.getmodebands(image.mode) img = voxel_globe.meta.models.Image.create( name="Generic Upload %s (%s) Frame %s" % (uploadSession.name, uploadSession_id, basename), imageWidth=image_info['width'], imageHeight=image_info['height'], numberColorBands=image_info['bands'], pixelFormat=image_info['dtype'].char, fileFormat='zoom', imageUrl='%s://%s:%s/%s/%s/' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], relZoomPath), originalImageUrl='%s://%s:%s/%s/%s' % (env['VIP_IMAGE_SERVER_PROTOCOL'], env['VIP_IMAGE_SERVER_HOST'], env['VIP_IMAGE_SERVER_PORT'], env['VIP_IMAGE_SERVER_URL_PATH'], relFilePath), service_id = self.request.id); img.save(); imageCollection.images.add(img); origin = [0,0,0]; logger.debug('Origin is: %s' % origin) k = numpy.eye(3); k[0,2] = image_info['width']/2; k[1,2] = image_info['height']/2; save_krt(self.request.id, img, k, r, t, origin); voxel_globe.meta.models.Scene.create(name="Generic origin %s (%s)" % (uploadSession.name, uploadSession_id), service_id = self.request.id, geolocated=False, origin='POINT(%0.12f %0.12f %0.12f)' % \ (0,0,0)).save() uploadSession.delete()
def runVisualSfm(self, imageSetId, sceneId, cleanup=True): from voxel_globe.meta import models from os import environ as env from os.path import join as path_join import os import shutil import time from django.contrib.gis.geos import Point from .tools import writeNvm, writeGcpFile, generateMatchPoints, runSparse,\ readNvm import voxel_globe.tools from voxel_globe.tools.camera import get_kto, save_krt import voxel_globe.tools.enu as enu import numpy import boxm2_adaptor import boxm2_scene_adaptor from voxel_globe.tools.xml_dict import load_xml from django.contrib.gis.geos import Point from voxel_globe.tools.image import convert_image from distutils.spawn import find_executable from vsi.iglob import glob as glob self.update_state(state='INITIALIZE', meta={'stage':0}) #Make main temp dir and cd into it with voxel_globe.tools.task_dir('visualsfm', cd=True) as processing_dir: #Because visualsfm is so... bad, I need to copy it locally so I can #configure it visualsfm_exe = os.path.join(processing_dir, 'visualsfm') shutil.copy(find_executable('VisualSFM'), visualsfm_exe) with open(os.path.join(processing_dir, 'nv.ini'), 'w') as fid: fid.write('param_search_multiple_models 0\n') fid.write('param_use_siftgpu 2\n') matchFilename = path_join(processing_dir, 'match.nvm') sparce_filename = path_join(processing_dir, 'sparse.nvm') #This can NOT be changed in version 0.5.25 gcpFilename = matchFilename + '.gcp' logger.debug('Task %s is processing in %s' % (self.request.id, processing_dir)) image_set = models.ImageSet.objects.get( id=imageSetId) imageList = image_set.images.all() ### if 1: ### try: #Not fully integrated yet ### sift_gpu = siftgpu.SiftGPU() ### except: ### pass localImageList = [] for x in range(len(imageList)): #Download the image locally image = imageList[x] self.update_state(state='INITIALIZE', meta={'stage':'image fetch', 'i':x, 'total':len(imageList)}) imageName = image.filename_path extension = os.path.splitext(imageName)[1].lower() localName = path_join(processing_dir, 'frame_%05d%s' % (x+1, extension)) #lncp(imageName, localName) #Stupid VisualSFM dereferences symlinks, breaking this shutil.copyfile(imageName, localName) #Convert the image if necessary if extension not in ['.jpg', '.jpeg', '.pgm', '.ppm']: self.update_state(state='INITIALIZE', meta={'stage':'image convert', 'i':x, 'total':len(imageList)}) #Add code here to converty to jpg for visual sfm if extension in ['.png']:#'not implemented': from PIL import Image image_temp = Image.open(localName) # if len(image_temp.mode) > 1: #Stupid visual sfm is picky :( # new_local_name = os.path.splitext(localName)[0] + '.ppm' # else: # new_local_name = os.path.splitext(localName)[0] + '.pgm' new_local_name = os.path.splitext(localName)[0] + '.jpg' ###ingest.convert_image(localName, new_local_name, 'PNM') convert_image(localName, new_local_name, 'JPEG', options=('QUALITY=100',)) os.remove(localName) localName = new_local_name else: raise Exception('Unsupported file type') imageInfo = {'localName':localName, 'index':x} try: [K, T, llh] = get_kto(image) imageInfo['K_intrinsics'] = K imageInfo['transformation'] = T imageInfo['enu_origin'] = llh except: pass localImageList.append(imageInfo) ### if 1: ### try: #not fully integrated yet ### sift_gpu.create_sift(localName, os.path.splitext(localName)[0]+'.sift') ### except: ### pass # filenames = list(imageList.values_list('image_url')) # logger.info('The image list 0is %s' % filenames) self.update_state(state='PROCESSING', meta={'stage':'generate match points', 'processing_dir':processing_dir, 'total':len(imageList)}) pid = generateMatchPoints(map(lambda x:x['localName'], localImageList), matchFilename, logger=logger, executable=visualsfm_exe) old_mat=None old_sift=None #TODO: Replace with inotify to monitor directory while pid.poll() is None: mat = len(glob(os.path.join(processing_dir, '*.mat'), False)) sift = len(glob(os.path.join(processing_dir, '*.sift'), False)) if mat != old_mat or \ sift != old_sift: old_mat=mat old_sift=sift self.update_state(state='PROCESSING', meta={'stage':'generate match points', 'processing_dir':processing_dir, 'sift':sift, 'mat':mat, 'total':len(imageList)}) time.sleep(5) # cameras = [] # for image in imageList: # if 1: # #try: # [K, T, llh] = get_kto(image) # cameras.append({'image':image.id, 'K':K, 'tranformation': # T, 'origin':llh}) # #except: # pass # origin = numpy.median(origin, axis=0) # origin = [-92.215197, 37.648858, 268.599] scene = models.Scene.objects.get(id=sceneId) origin = list(scene.origin) if scene.geolocated: self.update_state(state='PROCESSING', meta={'stage':'writing gcp points'}) #find the middle origin, and make it THE origin data = []#.name .llh_xyz for imageInfo in localImageList: try: r = imageInfo['transformation'][0:3, 0:3] t = imageInfo['transformation'][0:3, 3:] enu_point = -r.transpose().dot(t) if not numpy.array_equal(imageInfo['enu_origin'], origin): ecef = enu.enu2xyz(refLong=imageInfo['enu_origin'][0], refLat=imageInfo['enu_origin'][1], refH=imageInfo['enu_origin'][2], #e=imageInfo['transformation'][0, 3], #n=imageInfo['transformation'][1, 3], #u=imageInfo['transformation'][2, 3]) e=enu_point[0], n=enu_point[1], u=enu_point[2]) enu_point = enu.xyz2enu(refLong=origin[0], refLat=origin[1], refH=origin[2], X=ecef[0], Y=ecef[1], Z=ecef[2]) # else: # enu_point = imageInfo['transformation'][0:3, 3] dataBit = {'filename':imageInfo['localName'], 'xyz':enu_point} data.append(dataBit) #Make this a separate ingest process, making CAMERAS linked to the #images #data = arducopter.loadAdjTaggedMetadata( # r'd:\visualsfm\2014-03-20 13-22-44_adj_tagged_images.txt') #Make this read the cameras from the DB instead writeGcpFile(data, gcpFilename) except: #some images may have no camera pass self.update_state(state='PROCESSING', meta={'stage':'sparse SFM'}) pid = runSparse(matchFilename, sparce_filename, gcp=scene.geolocated, shared=True, logger=logger, executable=visualsfm_exe) pid.wait() self.update_state(state='FINALIZE', meta={'stage':'loading resulting cameras'}) #prevent bundle2scene from getting confused and crashing sift_data = os.path.join(processing_dir, 'sift_data') os.mkdir(sift_data) for filename in glob(os.path.join(processing_dir, '*.mat'), False) +\ glob(os.path.join(processing_dir, '*.sift'), False): shutil.move(filename, sift_data) if scene.geolocated: #Create a uscene.xml for the geolocated case. All I want out of this is #the bounding box and gsd calculation. boxm2_adaptor.bundle2scene(sparce_filename, processing_dir, isalign=False, out_dir="") cams = readNvm(path_join(processing_dir, 'sparse.nvm')) #cams.sort(key=lambda x:x.name) #Since the file names are frame_00001, etc... and you KNOW this order is #identical to localImageList, with some missing camera_set = models.CameraSet(name="Visual SFM Geo %s" % image_set.name, service_id = self.request.id, images_id = imageSetId) camera_set.save() for cam in cams: frameName = cam.name #frame_00001, etc.... imageInfo = filter(lambda x: x['localName'].endswith(frameName), localImageList)[0] #I have to use endswith instead of == because visual sfm APPARENTLY #decides to take some liberty and make absolute paths relative image = imageList[imageInfo['index']] (k,r,t) = cam.krt(width=image.image_width, height=image.image_height) t = t.flatten() camera = save_krt(self.request.id, image, k, r, t, origin, srid=4326) camera_set.cameras.add(camera) else: from vsi.tools.natural_sort import natural_sorted from vsi.io.krt import Krt boxm2_adaptor.bundle2scene(sparce_filename, processing_dir, isalign=True, out_dir=processing_dir) #While the output dir is used for the b2s folders, uscene.xml is cwd #They are both set to processing_dir, so everything works out well aligned_cams = glob(os.path.join(processing_dir, 'cams_krt', '*')) #sort them naturally in case there are more then 99,999 files aligned_cams = natural_sorted(aligned_cams) if len(aligned_cams) != len(imageList): #Create a new image set new_image_set = models.ImageSet( name="SFM Result Subset (%s)" % image_set.name, service_id = self.request.id) # for image in image_set.images.all(): # new_image_set.images.add(image) new_image_set.save() frames_keep = set(map(lambda x: int(os.path.splitext(x.split('_')[-2])[0])-1, aligned_cams)) for frame_index in frames_keep: new_image_set.images.add(imageList[frame_index]) # frames_remove = set(xrange(len(imageList))) - frames_keep # # for remove_index in list(frames_remove): # #The frame number refers to the nth image in the image set, # #so frame_00100.tif is the 100th image, starting the index at one # #See local_name above # # #remove the images sfm threw away # new_image_set.remove(imageList[remove_index]) image_set = new_image_set frames_keep = list(frames_keep) else: frames_keep = xrange(len(aligned_cams)) camera_set = models.CameraSet(name="Visual SFM %s" % image_set.name, service_id = self.request.id, images_id = imageSetId) camera_set.save() #---Update the camera models in the database.--- for camera_index, frame_index in enumerate(frames_keep): krt = Krt.load(aligned_cams[camera_index]) image = imageList[frame_index] camera = save_krt(self.request.id, image, krt.k, krt.r, krt.t, [0,0,0], srid=4326) camera_set.cameras.add(camera) #---Update scene information important for the no-metadata case --- scene_filename = os.path.join(processing_dir, 'model', 'uscene.xml') boxm_scene = boxm2_scene_adaptor.boxm2_scene_adaptor(scene_filename) scene.bbox_min = Point(*boxm_scene.bbox[0]) scene.bbox_max = Point(*boxm_scene.bbox[1]) #This is not a complete or good function really... but it will get me the #information I need. scene_dict = load_xml(scene_filename) block = scene_dict['block'] scene.default_voxel_size=Point(float(block.at['dim_x']), float(block.at['dim_y']), float(block.at['dim_z'])) scene.save()