Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blur face #12

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ Requirements:
- nibabel
- nipype

####To install:
#### To install:

* git clone https://github.com/poldracklab/pydeface.git
* cd pydeface
* python setup.py install

####To use:
#### To use:

pydeface.py infile.nii.gz
File renamed without changes.
22 changes: 0 additions & 22 deletions pydeface/utils.py

This file was deleted.

187 changes: 112 additions & 75 deletions scripts/pydeface.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python
""" deface an image using FSL
USAGE: deface <filename to deface> <optional: outfilename>
"""
deface an image using FSL
USAGE: deface -i <filename to deface> -o [output filename]
"""

## Copyright 2011, Russell Poldrack. All rights reserved.
Expand All @@ -26,118 +27,154 @@
## ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



import nibabel
import os,sys
import numpy as N
import os
import sys
import tempfile
import subprocess
import inspect
import argparse
import nibabel
from scipy import ndimage as nd
from nipype.interfaces import fsl
from pkg_resources import resource_filename,Requirement
from pkg_resources import resource_filename, Requirement


def run_shell_cmd(cmd,cwd=[]):
""" run a command in the shell using Popen
"""
Run a command in the shell using Popen
"""
if cwd:
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,cwd=cwd)
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, cwd=cwd)
else:
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
for line in process.stdout:
print((line.strip()))
process.wait()
print(line.strip())

def usage():
""" print the docstring and exit"""
sys.stdout.write(__doc__)
sys.exit(2)
process.wait()


def main():
cleanup=True
verbose=False
scriptdir=os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) # script directory

template=resource_filename(Requirement.parse("pydeface"), 'pydeface/data/mean_reg2mean.nii.gz')
facemask=resource_filename(Requirement.parse("pydeface"), "pydeface/data/facemask.nii.gz")
# T1w template and face mask locations
T1w_template = resource_filename(Requirement.parse("pydeface"), 'pydeface/data/T1w_template.nii.gz')
facemask = resource_filename(Requirement.parse("pydeface"), "pydeface/data/facemask.nii.gz")

try:
assert os.path.exists(facemask)
except:
raise Exception('missing facemask: %s'%facemask)

try:
assert os.path.exists(template)
assert os.path.exists(T1w_template)
except:
raise Exception('missing template: %s'%template)



if len(sys.argv)<2:
usage()
sys.exit(2)
else:
infile=sys.argv[1]
raise Exception('*** Missing template : %s'%T1w_template)

if len(sys.argv)>2:
outfile=sys.argv[2]
else:
outfile=infile.replace('.nii.gz','_defaced.nii.gz')
try:
assert not os.path.exists(outfile)
assert os.path.exists(facemask)
except:
raise Exception('%s already exists, remove it first'%outfile)
raise Exception('*** Missing facemask : %s'%facemask)

# Check that FSLDIR is set
if 'FSLDIR' in os.environ:
FSLDIR=os.environ['FSLDIR']
else:
print('FSL must be installed and FSLDIR environment variable must be defined')
sys.exit(2)

# Command line argument parser
parser = argparse.ArgumentParser(description='Remove facial structure from MRI images')
parser.add_argument('-i', '--infile', required=True, help='T1w input image')
parser.add_argument('-o', '--outfile', required=False, help='Defaced output image [<infile>_defaced.nii.gz]')
parser.add_argument('-s', '--scalefactor', required=False, help='Voxelation scale factor [8.0]')

# Parse command line arguments
args = parser.parse_args()

# Input T1w image filename
T1w_fname = args.infile

# Create output filename if not supplied
if args.outfile:
T1w_defaced_fname = args.outfile
else:
T1w_defaced_fname = T1w_fname.replace('.nii.gz','_defaced.nii.gz')

if args.scalefactor:
vox_sf = float(args.scalefactor)
else:
vox_sf = 8.0

# Check if output file already exists
if os.path.isfile(T1w_defaced_fname):
print('%s already exists - remove it first' % T1w_defaced_fname)
sys.exit(1)

foo,tmpmat=tempfile.mkstemp()
tmpmat=tmpmat+'.mat'
foo,tmpfile=tempfile.mkstemp()
tmpfile=tmpfile+'.nii.gz'
if verbose:
print(tmpmat)
print(tmpfile)
foo,tmpfile2=tempfile.mkstemp()
foo,tmpmat2=tempfile.mkstemp()
# Temporary template to individual affine transform matrix
_, temp2ind_mat_fname = tempfile.mkstemp()
temp2ind_mat_fname += '.mat'

print(('defacing', infile))
# register template to infile
# Temporal face mask in individual space
_, indmask_fname_fname = tempfile.mkstemp()
indmask_fname_fname += '.nii.gz'

flirt=fsl.FLIRT()
# Dummy output data
_, dummy_img_fname = tempfile.mkstemp()
_, dummy_mat_fname = tempfile.mkstemp()

print('Defacing %s' % T1w_fname)

# Register template to infile
print('Registering template to individual space')
flirt = fsl.FLIRT()
flirt.inputs.cost_func='mutualinfo'
flirt.inputs.in_file=template
flirt.inputs.out_matrix_file=tmpmat
flirt.inputs.out_file=tmpfile2
flirt.inputs.reference=infile
flirt.inputs.in_file = T1w_template
flirt.inputs.out_matrix_file = temp2ind_mat_fname
flirt.inputs.reference = T1w_fname
flirt.inputs.out_file = dummy_img_fname
flirt.run()

# warp facemask to infile
flirt=fsl.FLIRT()
flirt.inputs.in_file=facemask
flirt.inputs.in_matrix_file=tmpmat
flirt.inputs.apply_xfm=True
flirt.inputs.reference=infile
flirt.inputs.out_file=tmpfile
flirt.inputs.out_matrix_file=tmpmat2
# Affine transform facemask to infile
print('Resampling face mask to individual space')
flirt = fsl.FLIRT()
flirt.inputs.in_file = facemask
flirt.inputs.in_matrix_file = temp2ind_mat_fname
flirt.inputs.apply_xfm = True
flirt.inputs.reference = T1w_fname
flirt.inputs.out_file = indmask_fname_fname
flirt.inputs.out_matrix_file = dummy_mat_fname
flirt.run()

# multiply mask by infile and save

infile_img=nibabel.load(infile)
tmpfile_img=nibabel.load(tmpfile)
outdata=infile_img.get_data()*tmpfile_img.get_data()
outfile_img=nibabel.Nifti1Image(outdata,infile_img.get_affine(),infile_img.get_header())
outfile_img.to_filename(outfile)
# Load T1w input image
print('Loading T1w image : %s' % T1w_fname)
T1w_nii = nibabel.load(T1w_fname)
T1w_img = T1w_nii.get_data()

# Voxelate T1w image
# 1. Cubic downsample
# 2. Nearest neighbor upsample
print('Voxelating T1w image : scale factor %0.1f' % vox_sf)
print(' Spline downsampling')
T1w_vox_img = nd.interpolation.zoom(T1w_img, zoom=1.0/vox_sf, order=3)
print(' Nearest neighbor upsampling')
T1w_vox_img = nd.interpolation.zoom(T1w_vox_img, zoom=vox_sf, order=0)

# Load individual space face mask
indmask_nii = nibabel.load(indmask_fname_fname)
indmask_img = indmask_nii.get_data()

# Replace face area with voxelated version
# Note that the face mask is 0 in the face region, 1 elsewhere.
print('Anonymizing face area')
T1w_defaced_img = T1w_img * indmask_img + T1w_vox_img * (1 - indmask_img)

# Save defaced image
print('Saving defaced image : %s' % T1w_defaced_fname)
outfile_img = nibabel.Nifti1Image(T1w_defaced_img, T1w_nii.get_affine(), T1w_nii.get_header())
outfile_img.to_filename(T1w_defaced_fname)

# Cleanup temporary files
print('Cleaning up')
os.remove(temp2ind_mat_fname)
os.remove(indmask_fname_fname)
os.remove(dummy_img_fname)
os.remove(dummy_mat_fname)

if cleanup:
os.remove(tmpfile)
os.remove(tmpfile2)
os.remove(tmpmat)

if __name__ == "__main__":
main()
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#! /usr/bin/env python
#!/usr/bin/env python
#
# Copyright (C) 2013-2015 Russell Poldrack <[email protected]>
# some portions borrowed from https://github.com/mwaskom/lyman/blob/master/setup.py
Expand All @@ -17,13 +17,13 @@
LICENSE='MIT'
URL='http://poldracklab.org'
DOWNLOAD_URL='https://github.com/poldracklab/pydeface/'
VERSION='1.0'
VERSION='1.1'

def check_dependencies():

# Just make sure dependencies exist, I haven't rigorously
# tested what the minimal versions that will work are
needed_deps = ["numpy", "nibabel","nipype"]
needed_deps = ["numpy", "nibabel", "nipype"]
missing_deps = []
for dep in needed_deps:
try:
Expand All @@ -47,7 +47,7 @@ def check_dependencies():
'clean'))):
check_dependencies()

datafiles = {'pydeface': ['data/facemask.nii.gz','data/mean_reg2mean.nii.gz']}
datafiles = {'pydeface': ['data/facemask.nii.gz','data/T1w_template.nii.gz', 'ident.mat']}

setup(name=DISTNAME,
maintainer=MAINTAINER,
Expand Down