def createMorph(selectedImages,selectedPMs): """ Translates and rotate the bitmaps based on the shapedefining landmarks (selectedPMs) of the associated image to the target image (first image). Morphs the result so that the bitmap overlay on the first image is valid for the first image. """ #save the temporary results here later: os_temp_path = tempfile.gettempdir() #get the measurements for the first image (our targets) mainImage = OriginalImage.objects.all().get(id=selectedImages[0]) potentialids = [] #now get the associated measurements measures = Measurement.objects.all().filter(id__in=selectedPMs[0]).filter(mogelijkemeting__shapedefining=True) measures = [j for j in measures] measures.sort(key=lambda x: x.mogelijkemeting.name) coordsx = [] coordsy = [] for k, measurement in enumerate(measures): coordsx.append(float(measurement.x)) coordsy.append(float(measurement.y)) potentialids.append(measurement.mogelijkemeting.id) r1 = vtk.vtkJPEGReader() r1.SetFileName(settings.DATADIR + mainImage.id + ".jpg") r1.Update() # flip y coord (VTK has opposite convention), create 3-d coords (z=0) ydim = r1.GetOutput().GetDimensions()[1] coords = [(x, ydim - y, 0) for (x,y) in zip(coordsx, coordsy)] # convert everything to vtkPoints lmt = vtk.vtkPoints() lmt.SetNumberOfPoints(len(coords)) for i, coord in enumerate(coords): lmt.SetPoint(i,coord) #The target is clear, let's get to work, get the source images... images = [] #we don't need the first image or its measures anymore, because they don't need to be transformed or morphed selectedImages.pop(0) selectedPMs.pop(0) for id in selectedImages: images.append(OriginalImage.objects.all().get(id=id)) transformations = [] morphtransformations = [] #Create a new database object for the target image to associate the bitmaps with img = OriginalImage(project=mainImage.project, name='MorphedImage') img.save() imp = Image.open(settings.DATADIR + mainImage.id + '.jpg') imp.save(settings.DATADIR + img.id + '.jpg', 'JPEG') orig_bitmaps = Bitmap.objects.all().filter(image=mainImage) for bm in orig_bitmaps: #store bitmaps of mainImage as sub of img bitmap = Bitmap(project=img.project, name='warpedbitmap', image=img, mogelijkemeting=bm.mogelijkemeting, imagewidth=bm.imagewidth, imageheight=bm.imageheight, minx=bm.minx, miny=bm.miny, maxx=bm.maxx, maxy=bm.maxy) bitmap.save() bitmap_image = Image.open(settings.DATADIR + bm.id + '.gif') bitmap_image = bitmap_image.convert("RGBA") bitmap_image.save(settings.DATADIR + bitmap.id + '.gif', transparency=0) #now get the other images and perform our transformations for i in range(len(images)): measures = Measurement.objects.all().filter(id__in=selectedPMs[i]).filter(mogelijkemeting__shapedefining=True)#get measurements measures = [j for j in measures] measures.sort(key=lambda x: x.mogelijkemeting.name) coordsx = [] coordsy = [] for k, measurement in enumerate(measures): coordsx.append(float(measurement.x)) coordsy.append(float(measurement.y)) if potentialids[k] != measurement.mogelijkemeting.id: #the potentialmeasurements do not match up to the ones in the target image return img, 0 r = vtk.vtkJPEGReader() r.SetFileName(settings.DATADIR + images[i].id + ".jpg") r.Update() ydim = r.GetOutput().GetDimensions()[1] coordso = [(x, ydim - y, 0) for (x,y) in zip(coordsx, coordsy)] lms = vtk.vtkPoints() lms.SetNumberOfPoints(len(coordso)) for k, coord in enumerate(coordso): lms.SetPoint(k,coord) transformation = vtk.vtkLandmarkTransform() transformation.SetTargetLandmarks(lmt) lmt.Modified() transformation.SetSourceLandmarks(lms) lms.Modified() #size matters, so set the mode to Rigid Body (also known as do not scale please) transformation.SetModeToRigidBody() transformation.Inverse() transformation.Update() out = vtk.vtkPoints()#this will be the source of our morph transform transformation.TransformPoints(lms,out) transformations.append(transformation) ir = vtk.vtkImageReslice() # we're not using linear, because we want to improve the quality of the bitmaps ir.SetInterpolationModeToNearestNeighbor() ir.SetResliceTransform(transformation) ir.SetInput(r.GetOutput()) ir.SetInformationInput(r1.GetOutput()) w = vtk.vtkJPEGWriter() w.SetFileName(os_temp_path+'/translated'+images[i].id+'.jpg') w.SetInput(ir.GetOutput()) w.Write() r2 = vtk.vtkJPEGReader() r2.SetFileName(os_temp_path+'/translated'+images[i].id+'.jpg') r2.Update() # the mighty morphing ThinPlateSplineTransform morphtransform = vtk.vtkThinPlateSplineTransform() morphtransform.SetBasisToR2LogR() morphtransform.SetSourceLandmarks(lms) lms.Modified() morphtransform.SetTargetLandmarks(lmt) lmt.Modified() morphtransform.Inverse() morphtransform.Update() morphtransformations.append(morphtransform) #ir.SetInput(r2.GetOutput()) #ir.SetInformationInput(r1.GetOutput()) bitmaps = Bitmap.objects.all().filter(image=images[i]) #now perform the total transformation on all bitmaps for bm in bitmaps: location = settings.DATADIR + bm.id + ".gif" im = Image.open(location) im = im.convert("RGBA") im.save(settings.DATADIR + bm.id + ".png", "PNG") r3 = vtk.vtkPNGReader() r3.SetFileName(settings.DATADIR + bm.id + '.png') r3.Update() ir2 = vtk.vtkImageReslice() ir2.SetInterpolationModeToNearestNeighbor() ir2.SetResliceTransform(morphtransform) ir2.SetInput(r3.GetOutput()) ir2.SetInformationInput(r2.GetOutput()) w3 = vtk.vtkPNGWriter() w3.SetFileName(os_temp_path+'/morphed'+bm.id+'.png') w3.SetInput(ir2.GetOutput()) w3.Write() bitmap = Bitmap(project=img.project, name='warpedbitmap', image=img, mogelijkemeting=bm.mogelijkemeting, imagewidth=bm.imagewidth, imageheight=bm.imageheight, minx=bm.minx, miny=bm.miny, maxx=bm.maxx, maxy=bm.maxy) bitmap.save() im = Image.open(os_temp_path+'/morphed'+bm.id+'.png') im = im.convert("RGBA") im.save(settings.DATADIR + bitmap.id + '.gif', transparency=0) return img, 1
def handle_bitmap_stream(dump,original_image,previous_id,r,g,b,mm): """ With the string representation of a bitmap; the original_image this bitmap is painted over; the id of the previous bitmap; the color information and the id of the possible measurement this function saves the dump as a .gif-file and puts it in the db. """ # Check the header if dump == "_empty": print "Empty bitmap submitted" return previous_id # Initialise color information if not (r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255): r = 255 g = 0 b = 0 #backgroundColor = ((r << 16) & 0xFF) + ((g << 8) & 0xFF) + (b & 0xFF); # Split dump in header and body dump = dump.split("#") header = dump[0].split("x") body = dump[1] # Check the header if header[0] != "_scanline:": print "wrong dump" return # Load needed variables from header total_width = int(header[1]) total_height = int(header[2]) min_x = int(header[3]) max_x = int(header[4]) min_y = int(header[5]) max_y = int(header[6]) # Derive needed variables from loaded variables block_width = max_x - min_x + 1 block_height = max_y - min_y + 1 skip_before = min_y * total_width + min_x skip_between = min_x + (total_width - 1 - max_x) skip_after = (total_height - 1 - max_y) * total_width + (total_width - 1 - max_x) # Create string to fill before and after block fill_before = ''.zfill(skip_before).replace('0',chr(0)) fill_after = ''.zfill(skip_after).replace('0',chr(0)) # Keep track of the amount of pixels that is currently on the line # in the block to be able to determine how often we need to add skip_between line_fill = 0 # Make decode bit-stream stream = fill_before # Do as long as there is still data in body while len(body) > 1: # Find number of black pixels end = body.find("b") i = int(body[:end]) body = body[end+1:] # We need at least to add i pixels line_fill += i # Each whole amount block_with fits in line_fill, # we need to add skip_between while line_fill > block_width: i += skip_between line_fill -= block_width # Write i background pixels to the bit-stream stream += ''.zfill(i).replace('0',chr(0)) # Stop if we now finished the end of the string data if len(body) < 2: break; # Now do the foreground end = body.find("f") i = int(body[:end]) body = body[end+1:] # If we now need to add lines, it should # also be background outside the block if line_fill + i > block_width: # First write foreground pixels to fill the current row in the block stream += ''.zfill(block_width - line_fill).replace('0', chr(255)) # We still have this much pixels to write line_fill = i - (block_width - line_fill) # As long as we can fill full block lines while line_fill > block_width: # First write background line to get back to block stream += ''.zfill(skip_between).replace('0', chr(0)) # Then write a full line foreground-data stream += ''.zfill(block_width).replace('0', chr(255)) line_fill -= block_width # Return to a new line (by filling space outside the block) stream += ''.zfill(skip_between).replace('0', chr(0)) # We still have a number of pixels left to write i = line_fill # Otherwise we also need to remember this set of pixels else: line_fill += i # Write to the bit-stream stream += ''.zfill(i).replace('0',chr(255)) stream += fill_after # Build image im = Image.fromstring("L",(total_width,total_height), stream, "raw", "L") # Attach palette im.convert("P") palette = [] for i in range(256): palette.extend((0, 0, 0)) palette[0] = 0 palette[1] = 0 palette[2] = 0 palette[765] = r palette[766] = g palette[767] = b assert len(palette) == 768 im.putpalette(palette) # Make database insert if previous_id == '0': # Create associated measurement properties = dict( image = original_image, mogelijkemeting = mm, imagewidth = int(total_width), imageheight = int(total_height), minx = min_x, maxx = max_x, miny = min_y, maxy = max_y, project = original_image.project, name = "bitmap", ) db_bitmap = Bitmap(**properties) db_bitmap.save() img_path = os.path.join(settings.DATADIR, db_bitmap.id + ".gif") im.save(img_path, transparency=0) return db_bitmap.id # Just overwrite previous image-file else: previous_image = Bitmap.objects.all().get(id=previous_id) previous_image.minx = min_x previous_image.maxx = max_x previous_image.miny = min_y previous_image.maxy = max_y previous_image.save() image_path = os.path.join(settings.DATADIR, previous_image.id + '.gif') im.save(image_path, transparency=0) return previous_id