natanielruiz
2017-09-13 c495a0f6b13b794bab9f6e3423d5038ce645d816
Batch testing and hopenet training complete
5个文件已添加
5个文件已修改
909 ■■■■■ 已修改文件
code/batch_testing.py 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/batch_testing/batch_testing_AFLW_preangles.py 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/batch_testing_preangles.py 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/hopenet.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/test.py 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/test_biwi_preangles.py 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/test_on_video.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/train.py 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/train_AFLW_preangles.py 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/train_preangles.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
code/batch_testing.py
New file
@@ -0,0 +1,147 @@
import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
import torch.backends.cudnn as cudnn
import torchvision
import torch.nn.functional as F
import cv2
import matplotlib.pyplot as plt
import sys
import os
import argparse
import datasets
import hopenet
import utils
import glob
def parse_args():
    """Parse input arguments."""
    parser = argparse.ArgumentParser(description='Head pose estimation using the Hopenet network.')
    parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',
            default=0, type=int)
    parser.add_argument('--data_dir', dest='data_dir', help='Directory path for data.',
          default='', type=str)
    parser.add_argument('--filename_list', dest='filename_list', help='Path to text file containing relative paths for every example.',
          default='', type=str)
    parser.add_argument('--snapshot_folder', dest='snapshot_folder', help='Name of model snapshot folder.',
          default='', type=str)
    parser.add_argument('--batch_size', dest='batch_size', help='Batch size.',
          default=1, type=int)
    parser.add_argument('--save_viz', dest='save_viz', help='Save images with pose cube.',
          default=False, type=bool)
    parser.add_argument('--iter_ref', dest='iter_ref', help='Number of iterative refinement passes.',
          default=1, type=int)
    parser.add_argument('--dataset', dest='dataset', help='Dataset type.', default='AFLW2000', type=str)
    args = parser.parse_args()
    return args
if __name__ == '__main__':
    args = parse_args()
    cudnn.enabled = True
    gpu = args.gpu_id
    # ResNet101 with 3 outputs.
    # model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 23, 3], 66)
    # ResNet50
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66, args.iter_ref)
    # ResNet18
    # model = hopenet.Hopenet(torchvision.models.resnet.BasicBlock, [2, 2, 2, 2], 66)
    print 'Loading snapshot list.'
    # Load snapshot
    snapshot_list = sorted(glob.glob(os.path.join(args.snapshot_folder, '*.pkl')))
    print 'Loading data.'
    transformations = transforms.Compose([transforms.Scale(224),
    transforms.RandomCrop(224), transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])
    if args.dataset == 'AFLW2000':
        pose_dataset = datasets.AFLW2000(args.data_dir, args.filename_list,
                                transformations)
    elif args.dataset == 'BIWI':
        pose_dataset = datasets.BIWI(args.data_dir, args.filename_list, transformations)
    elif args.dataset == 'AFLW':
        pose_dataset = datasets.AFLW(args.data_dir, args.filename_list, transformations)
    elif args.dataset == 'AFW':
        pose_dataset = datasets.AFW(args.data_dir, args.filename_list, transformations)
    else:
        print 'Error: not a valid dataset name'
        sys.exit()
    test_loader = torch.utils.data.DataLoader(dataset=pose_dataset,
                                               batch_size=args.batch_size,
                                               num_workers=2)
    model.cuda(gpu)
    print 'Ready to test network.'
    prefix = args.snapshot_folder.split('/')[-1]
    if prefix == '':
        prefix = args.snapshot_folder.split('/')[-2]
    output_file_name = prefix + '_' + args.dataset + '_angles.txt'
    txt_output = open(os.path.join('output/batch_snapshots', output_file_name), 'w')
    for snapshot_path in snapshot_list:
        snapshot_name = snapshot_path.split('/')[-1].split('.')[0]
        print 'Loading snapshot ' + snapshot_name
        saved_state_dict = torch.load(snapshot_path)
        model.load_state_dict(saved_state_dict)
        # Test the Model
        model.eval()  # Change model to 'eval' mode (BN uses moving mean/var).
        total = 0
        n_margins = 20
        yaw_correct = np.zeros(n_margins)
        pitch_correct = np.zeros(n_margins)
        roll_correct = np.zeros(n_margins)
        idx_tensor = [idx for idx in xrange(66)]
        idx_tensor = torch.FloatTensor(idx_tensor).cuda(gpu)
        yaw_error = .0
        pitch_error = .0
        roll_error = .0
        l1loss = torch.nn.L1Loss(size_average=False)
        for i, (images, labels, name) in enumerate(test_loader):
            images = Variable(images).cuda(gpu)
            total += labels.size(0)
            label_yaw = labels[:,0].float()
            label_pitch = labels[:,1].float()
            label_roll = labels[:,2].float()
            pre_yaw, pre_pitch, pre_roll, angles = model(images)
            yaw = angles[args.iter_ref-1][:,0].cpu().data
            pitch = angles[args.iter_ref-1][:,1].cpu().data
            roll = angles[args.iter_ref-1][:,2].cpu().data
            # Mean absolute error
            yaw_error += torch.sum(torch.abs(yaw - label_yaw) * 3)
            pitch_error += torch.sum(torch.abs(pitch - label_pitch) * 3)
            roll_error += torch.sum(torch.abs(roll - label_roll) * 3)
            if args.save_viz:
                name = name[0]
                cv2_img = cv2.imread(os.path.join(args.data_dir, name + '.jpg'))
                utils.plot_pose_cube(cv2_img, yaw_predicted[0] * 3 - 99, pitch_predicted[0] * 3 - 99, roll_predicted[0] * 3 - 99)
                cv2.imwrite(os.path.join('output/images', name + '.jpg'), cv2_img)
        print('Test error in degrees of the model on the ' + str(total) +
        ' test images. Yaw: %.4f, Pitch: %.4f, Roll: %.4f' % (yaw_error / total,
        pitch_error / total, roll_error / total))
        txt_output.write('Test error in degrees of model ' + snapshot_name + ' on the ' + str(total) +
        ' test images. Yaw: %.4f, Pitch: %.4f, Roll: %.4f \n' % (yaw_error / total,
        pitch_error / total, roll_error / total))
    txt_output.close()
code/batch_testing/batch_testing_AFLW_preangles.py
New file
@@ -0,0 +1,147 @@
import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
import torch.backends.cudnn as cudnn
import torchvision
import torch.nn.functional as F
import cv2
import matplotlib.pyplot as plt
import sys
import os
import argparse
import datasets
import hopenet
import utils
import glob
def parse_args():
    """Parse input arguments."""
    parser = argparse.ArgumentParser(description='Head pose estimation using the Hopenet network.')
    parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',
            default=0, type=int)
    parser.add_argument('--data_dir', dest='data_dir', help='Directory path for data.',
          default='', type=str)
    parser.add_argument('--filename_list', dest='filename_list', help='Path to text file containing relative paths for every example.',
          default='', type=str)
    parser.add_argument('--snapshot_folder', dest='snapshot_folder', help='Name of model snapshot folder.',
          default='', type=str)
    parser.add_argument('--batch_size', dest='batch_size', help='Batch size.',
          default=1, type=int)
    parser.add_argument('--save_viz', dest='save_viz', help='Save images with pose cube.',
          default=False, type=bool)
    args = parser.parse_args()
    return args
if __name__ == '__main__':
    args = parse_args()
    cudnn.enabled = True
    gpu = args.gpu_id
    # ResNet101 with 3 outputs.
    # model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 23, 3], 66)
    # ResNet50
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66)
    # ResNet18
    # model = hopenet.Hopenet(torchvision.models.resnet.BasicBlock, [2, 2, 2, 2], 66)
    print 'Loading snapshot list.'
    # Load snapshot
    snapshot_list = sorted(glob.glob(os.path.join(args.snapshot_folder, '*.pkl')))
    print 'Loading data.'
    # transformations = transforms.Compose([transforms.Scale(224),
    # transforms.RandomCrop(224), transforms.ToTensor()])
    transformations = transforms.Compose([transforms.Scale(224),
    transforms.RandomCrop(224), transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])
    pose_dataset = datasets.AFLW(args.data_dir, args.filename_list,
                                transformations)
    test_loader = torch.utils.data.DataLoader(dataset=pose_dataset,
                                               batch_size=args.batch_size,
                                               num_workers=2)
    model.cuda(gpu)
    print 'Ready to test network.'
    output_file_name = args.snapshot_folder.split('/')[-1] + '_AFLW_preangles.txt'
    txt_output = open(os.join('output/batch_snapshots', output_file_name), 'w')
    for snapshot_path in snapshot_list:
        snapshot_name = snapshot_path.split('/')[-1].split('.')[0]
        print 'Loading snapshot ' + snapshot_name
        saved_state_dict = torch.load(snapshot_path)
        model.load_state_dict(saved_state_dict)
        # Test the Model
        model.eval()  # Change model to 'eval' mode (BN uses moving mean/var).
        total = 0
        n_margins = 20
        yaw_correct = np.zeros(n_margins)
        pitch_correct = np.zeros(n_margins)
        roll_correct = np.zeros(n_margins)
        idx_tensor = [idx for idx in xrange(66)]
        idx_tensor = torch.FloatTensor(idx_tensor).cuda(gpu)
        yaw_error = .0
        pitch_error = .0
        roll_error = .0
        l1loss = torch.nn.L1Loss(size_average=False)
        for i, (images, labels, name) in enumerate(test_loader):
            images = Variable(images).cuda(gpu)
            total += labels.size(0)
            label_yaw = labels[:,0].float()
            label_pitch = labels[:,1].float()
            label_roll = labels[:,2].float()
            yaw, pitch, roll, angles = model(images)
            # Binned predictions
            _, yaw_bpred = torch.max(yaw.data, 1)
            _, pitch_bpred = torch.max(pitch.data, 1)
            _, roll_bpred = torch.max(roll.data, 1)
            # Continuous predictions
            yaw_predicted = utils.softmax_temperature(yaw.data, 1)
            pitch_predicted = utils.softmax_temperature(pitch.data, 1)
            roll_predicted = utils.softmax_temperature(roll.data, 1)
            yaw_predicted = torch.sum(yaw_predicted * idx_tensor, 1).cpu()
            pitch_predicted = torch.sum(pitch_predicted * idx_tensor, 1).cpu()
            roll_predicted = torch.sum(roll_predicted * idx_tensor, 1).cpu()
            # Mean absolute error
            yaw_error += torch.sum(torch.abs(yaw_predicted - label_yaw) * 3)
            pitch_error += torch.sum(torch.abs(pitch_predicted - label_pitch) * 3)
            roll_error += torch.sum(torch.abs(roll_predicted - label_roll) * 3)
            if args.save_viz:
                name = name[0]
                cv2_img = cv2.imread(os.path.join(args.data_dir, name + '.jpg'))
                utils.plot_pose_cube(cv2_img, yaw_predicted[0] * 3 - 99, pitch_predicted[0] * 3 - 99, roll_predicted[0] * 3 - 99)
                cv2.imwrite(os.path.join('output/images', name + '.jpg'), cv2_img)
        print('Test error in degrees of the model on the ' + str(total) +
        ' test images. Yaw: %.4f, Pitch: %.4f, Roll: %.4f' % (yaw_error / total,
        pitch_error / total, roll_error / total))
        txt_output.write('Test error in degrees of model ' + snapshot_name + ' on the ' + str(total) +
        ' test images. Yaw: %.4f, Pitch: %.4f, Roll: %.4f \n' % (yaw_error / total,
        pitch_error / total, roll_error / total))
    txt_output.close()
code/batch_testing_preangles.py
New file
@@ -0,0 +1,158 @@
import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
import torch.backends.cudnn as cudnn
import torchvision
import torch.nn.functional as F
import cv2
import matplotlib.pyplot as plt
import sys
import os
import argparse
import datasets
import hopenet
import utils
import glob
def parse_args():
    """Parse input arguments."""
    parser = argparse.ArgumentParser(description='Head pose estimation using the Hopenet network.')
    parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',
            default=0, type=int)
    parser.add_argument('--data_dir', dest='data_dir', help='Directory path for data.',
          default='', type=str)
    parser.add_argument('--filename_list', dest='filename_list', help='Path to text file containing relative paths for every example.',
          default='', type=str)
    parser.add_argument('--snapshot_folder', dest='snapshot_folder', help='Name of model snapshot folder.',
          default='', type=str)
    parser.add_argument('--batch_size', dest='batch_size', help='Batch size.',
          default=1, type=int)
    parser.add_argument('--save_viz', dest='save_viz', help='Save images with pose cube.',
          default=False, type=bool)
    parser.add_argument('--dataset', dest='dataset', help='Dataset type.', default='AFLW2000', type=str)
    args = parser.parse_args()
    return args
if __name__ == '__main__':
    args = parse_args()
    cudnn.enabled = True
    gpu = args.gpu_id
    # ResNet101 with 3 outputs.
    # model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 23, 3], 66)
    # ResNet50
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66)
    # ResNet18
    # model = hopenet.Hopenet(torchvision.models.resnet.BasicBlock, [2, 2, 2, 2], 66)
    print 'Loading snapshot list.'
    # Load snapshot
    snapshot_list = sorted(glob.glob(os.path.join(args.snapshot_folder, '*.pkl')))
    print 'Loading data.'
    transformations = transforms.Compose([transforms.Scale(224),
    transforms.RandomCrop(224), transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])
    if args.dataset == 'AFLW2000':
        pose_dataset = datasets.AFLW2000(args.data_dir, args.filename_list,
                                transformations)
    elif args.dataset == 'BIWI':
        pose_dataset = datasets.BIWI(args.data_dir, args.filename_list, transformations)
    elif args.dataset == 'AFLW':
        pose_dataset = datasets.AFLW(args.data_dir, args.filename_list, transformations)
    elif args.dataset == 'AFW':
        pose_dataset = datasets.AFW(args.data_dir, args.filename_list, transformations)
    else:
        print 'Error: not a valid dataset name'
        sys.exit()
    test_loader = torch.utils.data.DataLoader(dataset=pose_dataset,
                                               batch_size=args.batch_size,
                                               num_workers=2)
    model.cuda(gpu)
    print 'Ready to test network.'
    prefix = args.snapshot_folder.split('/')[-1]
    if prefix == '':
        prefix = args.snapshot_folder.split('/')[-2]
    output_file_name = prefix + '_' + args.dataset + '_preangles.txt'
    txt_output = open(os.path.join('output/batch_snapshots', output_file_name), 'w')
    for snapshot_path in snapshot_list:
        snapshot_name = snapshot_path.split('/')[-1].split('.')[0]
        print 'Loading snapshot ' + snapshot_name
        saved_state_dict = torch.load(snapshot_path)
        model.load_state_dict(saved_state_dict)
        # Test the Model
        model.eval()  # Change model to 'eval' mode (BN uses moving mean/var).
        total = 0
        n_margins = 20
        yaw_correct = np.zeros(n_margins)
        pitch_correct = np.zeros(n_margins)
        roll_correct = np.zeros(n_margins)
        idx_tensor = [idx for idx in xrange(66)]
        idx_tensor = torch.FloatTensor(idx_tensor).cuda(gpu)
        yaw_error = .0
        pitch_error = .0
        roll_error = .0
        l1loss = torch.nn.L1Loss(size_average=False)
        for i, (images, labels, name) in enumerate(test_loader):
            images = Variable(images).cuda(gpu)
            total += labels.size(0)
            label_yaw = labels[:,0].float()
            label_pitch = labels[:,1].float()
            label_roll = labels[:,2].float()
            yaw, pitch, roll, angles = model(images)
            # Binned predictions
            _, yaw_bpred = torch.max(yaw.data, 1)
            _, pitch_bpred = torch.max(pitch.data, 1)
            _, roll_bpred = torch.max(roll.data, 1)
            # Continuous predictions
            yaw_predicted = utils.softmax_temperature(yaw.data, 1)
            pitch_predicted = utils.softmax_temperature(pitch.data, 1)
            roll_predicted = utils.softmax_temperature(roll.data, 1)
            yaw_predicted = torch.sum(yaw_predicted * idx_tensor, 1).cpu()
            pitch_predicted = torch.sum(pitch_predicted * idx_tensor, 1).cpu()
            roll_predicted = torch.sum(roll_predicted * idx_tensor, 1).cpu()
            # Mean absolute error
            yaw_error += torch.sum(torch.abs(yaw_predicted - label_yaw) * 3)
            pitch_error += torch.sum(torch.abs(pitch_predicted - label_pitch) * 3)
            roll_error += torch.sum(torch.abs(roll_predicted - label_roll) * 3)
            if args.save_viz:
                name = name[0]
                cv2_img = cv2.imread(os.path.join(args.data_dir, name + '.jpg'))
                utils.plot_pose_cube(cv2_img, yaw_predicted[0] * 3 - 99, pitch_predicted[0] * 3 - 99, roll_predicted[0] * 3 - 99)
                cv2.imwrite(os.path.join('output/images', name + '.jpg'), cv2_img)
        print('Test error in degrees of the model on the ' + str(total) +
        ' test images. Yaw: %.4f, Pitch: %.4f, Roll: %.4f' % (yaw_error / total,
        pitch_error / total, roll_error / total))
        txt_output.write('Test error in degrees of model ' + snapshot_name + ' on the ' + str(total) +
        ' test images. Yaw: %.4f, Pitch: %.4f, Roll: %.4f \n' % (yaw_error / total,
        pitch_error / total, roll_error / total))
    txt_output.close()
code/hopenet.py
@@ -41,7 +41,7 @@
class Hopenet(nn.Module):
    # This is just Hopenet with 3 output layers for yaw, pitch and roll.
    def __init__(self, block, layers, num_bins):
    def __init__(self, block, layers, num_bins, iter_ref):
        self.inplanes = 64
        super(Hopenet, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
@@ -62,6 +62,8 @@
        self.fc_finetune = nn.Linear(512 * block.expansion + 3, 3)
        self.idx_tensor = Variable(torch.FloatTensor(range(66))).cuda()
        self.iter_ref = iter_ref
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
@@ -117,7 +119,7 @@
        angles = []
        angles.append(torch.cat([yaw, pitch, roll], 1))
        for idx in xrange(1):
        for idx in xrange(self.iter_ref):
            angles.append(self.fc_finetune(torch.cat((angles[-1], x), 1)))
        return pre_yaw, pre_pitch, pre_roll, angles
code/test.py
@@ -33,6 +33,7 @@
          default=1, type=int)
    parser.add_argument('--save_viz', dest='save_viz', help='Save images with pose cube.',
          default=False, type=bool)
    parser.add_argument('--iter_ref', dest='iter_ref', default=1, type=int)
    args = parser.parse_args()
@@ -48,7 +49,7 @@
    # ResNet101 with 3 outputs.
    # model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 23, 3], 66)
    # ResNet50
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66)
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66, args.iter_ref)
    # ResNet18
    # model = hopenet.Hopenet(torchvision.models.resnet.BasicBlock, [2, 2, 2, 2], 66)
@@ -98,9 +99,9 @@
        label_roll = labels[:,2].float()
        pre_yaw, pre_pitch, pre_roll, angles = model(images)
        yaw = angles[0][:,0].cpu().data
        pitch = angles[0][:,1].cpu().data
        roll = angles[0][:,2].cpu().data
        yaw = angles[args.iter_ref-1][:,0].cpu().data
        pitch = angles[args.iter_ref-1][:,1].cpu().data
        roll = angles[args.iter_ref-1][:,2].cpu().data
        # Mean absolute error
        yaw_error += torch.sum(torch.abs(yaw - label_yaw) * 3)
code/test_biwi_preangles.py
New file
@@ -0,0 +1,149 @@
import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
import torch.backends.cudnn as cudnn
import torchvision
import torch.nn.functional as F
import cv2
import matplotlib.pyplot as plt
import sys
import os
import argparse
import datasets
import hopenet
import utils
def parse_args():
    """Parse input arguments."""
    parser = argparse.ArgumentParser(description='Head pose estimation using the Hopenet network.')
    parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',
            default=0, type=int)
    parser.add_argument('--data_dir', dest='data_dir', help='Directory path for data.',
          default='', type=str)
    parser.add_argument('--filename_list', dest='filename_list', help='Path to text file containing relative paths for every example.',
          default='', type=str)
    parser.add_argument('--snapshot', dest='snapshot', help='Name of model snapshot.',
          default='', type=str)
    parser.add_argument('--batch_size', dest='batch_size', help='Batch size.',
          default=1, type=int)
    parser.add_argument('--save_viz', dest='save_viz', help='Save images with pose cube.',
          default=False, type=bool)
    args = parser.parse_args()
    return args
if __name__ == '__main__':
    args = parse_args()
    cudnn.enabled = True
    gpu = args.gpu_id
    snapshot_path = args.snapshot
    # ResNet101 with 3 outputs.
    # model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 23, 3], 66)
    # ResNet50
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66)
    # ResNet18
    # model = hopenet.Hopenet(torchvision.models.resnet.BasicBlock, [2, 2, 2, 2], 66)
    print 'Loading snapshot.'
    # Load snapshot
    saved_state_dict = torch.load(snapshot_path)
    model.load_state_dict(saved_state_dict)
    print 'Loading data.'
    # transformations = transforms.Compose([transforms.Scale(224),
    # transforms.RandomCrop(224), transforms.ToTensor()])
    transformations = transforms.Compose([transforms.Scale(224),
    transforms.CenterCrop(224), transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])
    pose_dataset = datasets.BIWI(args.data_dir, args.filename_list,
                                transformations)
    test_loader = torch.utils.data.DataLoader(dataset=pose_dataset,
                                               batch_size=args.batch_size,
                                               num_workers=2)
    model.cuda(gpu)
    print 'Ready to test network.'
    # Test the Model
    model.eval()  # Change model to 'eval' mode (BN uses moving mean/var).
    total = 0
    n_margins = 20
    yaw_correct = np.zeros(n_margins)
    pitch_correct = np.zeros(n_margins)
    roll_correct = np.zeros(n_margins)
    idx_tensor = [idx for idx in xrange(66)]
    idx_tensor = torch.FloatTensor(idx_tensor).cuda(gpu)
    yaw_error = .0
    pitch_error = .0
    roll_error = .0
    l1loss = torch.nn.L1Loss(size_average=False)
    for i, (images, labels, name) in enumerate(test_loader):
        images = Variable(images).cuda(gpu)
        total += labels.size(0)
        label_yaw = labels[:,0].float()
        label_pitch = labels[:,1].float()
        label_roll = labels[:,2].float()
        yaw, pitch, roll, angles = model(images)
        # Binned predictions
        _, yaw_bpred = torch.max(yaw.data, 1)
        _, pitch_bpred = torch.max(pitch.data, 1)
        _, roll_bpred = torch.max(roll.data, 1)
        # Continuous predictions
        yaw_predicted = utils.softmax_temperature(yaw.data, 1)
        pitch_predicted = utils.softmax_temperature(pitch.data, 1)
        roll_predicted = utils.softmax_temperature(roll.data, 1)
        yaw_predicted = torch.sum(yaw_predicted * idx_tensor, 1).cpu()
        pitch_predicted = torch.sum(pitch_predicted * idx_tensor, 1).cpu()
        roll_predicted = torch.sum(roll_predicted * idx_tensor, 1).cpu()
        # Mean absolute error
        yaw_error += torch.sum(torch.abs(yaw_predicted - label_yaw) * 3)
        pitch_error += torch.sum(torch.abs(pitch_predicted - label_pitch) * 3)
        roll_error += torch.sum(torch.abs(roll_predicted - label_roll) * 3)
        # Binned Accuracy
        # for er in xrange(n_margins):
        #     yaw_bpred[er] += (label_yaw[0] in range(yaw_bpred[0,0] - er, yaw_bpred[0,0] + er + 1))
        #     pitch_bpred[er] += (label_pitch[0] in range(pitch_bpred[0,0] - er, pitch_bpred[0,0] + er + 1))
        #     roll_bpred[er] += (label_roll[0] in range(roll_bpred[0,0] - er, roll_bpred[0,0] + er + 1))
        # print label_yaw[0], yaw_bpred[0,0]
        # Save images with pose cube.
        # TODO: fix for larger batch size
        if args.save_viz:
            name = name[0]
            cv2_img = cv2.imread(os.path.join(args.data_dir, name + '_rgb.png'))
            #print os.path.join('output/images', name + '.jpg')
            #print label_yaw[0] * 3 - 99, label_pitch[0] * 3 - 99, label_roll[0] * 3 - 99
            #print yaw_predicted * 3 - 99, pitch_predicted * 3 - 99, roll_predicted * 3 - 99
            utils.plot_pose_cube(cv2_img, yaw_predicted[0] * 3 - 99, pitch_predicted[0] * 3 - 99, roll_predicted[0] * 3 - 99)
            cv2.imwrite(os.path.join('output/images', name + '.jpg'), cv2_img)
    print('Test error in degrees of the model on the ' + str(total) +
    ' test images. Yaw: %.4f, Pitch: %.4f, Roll: %.4f' % (yaw_error / total,
    pitch_error / total, roll_error / total))
    # Binned accuracy
    # for idx in xrange(len(yaw_correct)):
    #     print yaw_correct[idx] / total, pitch_correct[idx] / total, roll_correct[idx] / total
code/test_on_video.py
@@ -141,7 +141,7 @@
        img_shape = img.size()
        img = img.view(1, img_shape[0], img_shape[1], img_shape[2])
        img = Variable(img).cuda(gpu)
        yaw, pitch, roll = model(img)
        yaw, pitch, roll, angles = model(img)
        yaw_predicted = F.softmax(yaw)
        pitch_predicted = F.softmax(pitch)
code/train.py
@@ -46,6 +46,8 @@
    parser.add_argument('--output_string', dest='output_string', help='String appended to output snapshots.', default = '', type=str)
    parser.add_argument('--alpha', dest='alpha', help='Regression loss coefficient.',
          default=0.001, type=float)
    parser.add_argument('--iter_ref', dest='iter_ref', help='Number of iterative refinement passes.',
          default=1, type=int)
    args = parser.parse_args()
    return args
@@ -111,7 +113,7 @@
    # ResNet101 with 3 outputs
    # model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 23, 3], 66)
    # ResNet50
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66)
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66, args.iter_ref)
    # ResNet18
    # model = hopenet.Hopenet(torchvision.models.resnet.BasicBlock, [2, 2, 2, 2], 66)
    load_filtered_state_dict(model, model_zoo.load_url(model_urls['resnet50']))
@@ -177,14 +179,12 @@
            loss_reg_pitch = reg_criterion(pitch_predicted, label_pitch.float())
            loss_reg_roll = reg_criterion(roll_predicted, label_roll.float())
            # print yaw_predicted, label_yaw.float(), loss_reg_yaw
            # Total loss
            loss_yaw += alpha * loss_reg_yaw
            loss_pitch += alpha * loss_reg_pitch
            loss_roll += alpha * loss_reg_roll
            loss_seq = [loss_yaw, loss_pitch, loss_roll]
            # loss_seq = [loss_reg_yaw, loss_reg_pitch, loss_reg_roll]
            grad_seq = [torch.Tensor(1).cuda(gpu) for _ in range(len(loss_seq))]
            torch.autograd.backward(loss_seq, grad_seq)
            optimizer.step()
@@ -226,9 +226,9 @@
            pitch_predicted = softmax(pre_pitch)
            roll_predicted = softmax(pre_roll)
            yaw_predicted = torch.sum(yaw_predicted.data * idx_tensor, 1)
            pitch_predicted = torch.sum(pitch_predicted.data * idx_tensor, 1)
            roll_predicted = torch.sum(roll_predicted.data * idx_tensor, 1)
            yaw_predicted = torch.sum(yaw_predicted * idx_tensor, 1)
            pitch_predicted = torch.sum(pitch_predicted * idx_tensor, 1)
            roll_predicted = torch.sum(roll_predicted * idx_tensor, 1)
            loss_reg_yaw = reg_criterion(yaw_predicted, label_yaw.float())
            loss_reg_pitch = reg_criterion(pitch_predicted, label_pitch.float())
@@ -240,9 +240,11 @@
            loss_roll += alpha * loss_reg_roll
            # Finetuning loss
            loss_angles = reg_criterion(angles[0], label_angles.float())
            loss_seq = [loss_yaw, loss_pitch, loss_roll]
            for idx in xrange(args.iter_ref):
                loss_angles = reg_criterion(angles[idx], label_angles.float())
                loss_seq.append(loss_angles)
            loss_seq = [loss_yaw, loss_pitch, loss_roll, loss_angles]
            grad_seq = [torch.Tensor(1).cuda(gpu) for _ in range(len(loss_seq))]
            torch.autograd.backward(loss_seq, grad_seq)
            optimizer.step()
@@ -255,11 +257,7 @@
                #     'output/snapshots/' + args.output_string + '_iter_'+ str(i+1) + '.pkl')
        # Save models at numbered epochs.
        if epoch % 1 == 0 and epoch < num_epochs_ft - 1:
        if epoch % 1 == 0 and epoch < num_epochs_ft:
            print 'Taking snapshot...'
            torch.save(model.state_dict(),
            'output/snapshots/' + args.output_string + '_epoch_'+ str(num_epochs+epoch+1) + '.pkl')
    # Save the final Trained Model
    torch.save(model.state_dict(), 'output/snapshots/' + args.output_string + '_epoch_' + str(num_epochs+epoch+1) + '.pkl')
code/train_AFLW_preangles.py
New file
@@ -0,0 +1,265 @@
import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
import torchvision
import torch.backends.cudnn as cudnn
import torch.nn.functional as F
import cv2
import matplotlib.pyplot as plt
import sys
import os
import argparse
import datasets
import hopenet
import torch.utils.model_zoo as model_zoo
model_urls = {
    'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
    'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
    'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
    'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
    'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
}
def parse_args():
    """Parse input arguments."""
    parser = argparse.ArgumentParser(description='Head pose estimation using the Hopenet network.')
    parser.add_argument('--gpu', dest='gpu_id', help='GPU device id to use [0]',
            default=0, type=int)
    parser.add_argument('--num_epochs', dest='num_epochs', help='Maximum number of training epochs.',
          default=5, type=int)
    parser.add_argument('--num_epochs_ft', dest='num_epochs_ft', help='Maximum number of finetuning epochs.',
          default=5, type=int)
    parser.add_argument('--batch_size', dest='batch_size', help='Batch size.',
          default=16, type=int)
    parser.add_argument('--lr', dest='lr', help='Base learning rate.',
          default=0.001, type=float)
    parser.add_argument('--data_dir', dest='data_dir', help='Directory path for data.',
          default='', type=str)
    parser.add_argument('--filename_list', dest='filename_list', help='Path to text file containing relative paths for every example.',
          default='', type=str)
    parser.add_argument('--output_string', dest='output_string', help='String appended to output snapshots.', default = '', type=str)
    parser.add_argument('--alpha', dest='alpha', help='Regression loss coefficient.',
          default=0.00, type=float)
    args = parser.parse_args()
    return args
def get_ignored_params(model):
    # Generator function that yields ignored params.
    b = []
    b.append(model.conv1)
    b.append(model.bn1)
    b.append(model.fc_finetune)
    for i in range(len(b)):
        for module_name, module in b[i].named_modules():
            if 'bn' in module_name:
                module.eval()
            for name, param in module.named_parameters():
                yield param
def get_non_ignored_params(model):
    # Generator function that yields params that will be optimized.
    b = []
    b.append(model.layer1)
    b.append(model.layer2)
    b.append(model.layer3)
    b.append(model.layer4)
    for i in range(len(b)):
        for module_name, module in b[i].named_modules():
            if 'bn' in module_name:
                module.eval()
            for name, param in module.named_parameters():
                yield param
def get_fc_params(model):
    b = []
    b.append(model.fc_yaw)
    b.append(model.fc_pitch)
    b.append(model.fc_roll)
    for i in range(len(b)):
        for module_name, module in b[i].named_modules():
            for name, param in module.named_parameters():
                yield param
def load_filtered_state_dict(model, snapshot):
    # By user apaszke from discuss.pytorch.org
    model_dict = model.state_dict()
    # 1. filter out unnecessary keys
    snapshot = {k: v for k, v in snapshot.items() if k in model_dict}
    # 2. overwrite entries in the existing state dict
    model_dict.update(snapshot)
    # 3. load the new state dict
    model.load_state_dict(model_dict)
if __name__ == '__main__':
    args = parse_args()
    cudnn.enabled = True
    num_epochs = args.num_epochs
    num_epochs_ft = args.num_epochs_ft
    batch_size = args.batch_size
    gpu = args.gpu_id
    if not os.path.exists('output/snapshots'):
        os.makedirs('output/snapshots')
    # ResNet101 with 3 outputs
    # model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 23, 3], 66)
    # ResNet50
    model = hopenet.Hopenet(torchvision.models.resnet.Bottleneck, [3, 4, 6, 3], 66)
    # ResNet18
    # model = hopenet.Hopenet(torchvision.models.resnet.BasicBlock, [2, 2, 2, 2], 66)
    load_filtered_state_dict(model, model_zoo.load_url(model_urls['resnet50']))
    print 'Loading data.'
    transformations = transforms.Compose([transforms.Scale(224),
    transforms.RandomCrop(224), transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])
    pose_dataset = datasets.AFLW(args.data_dir, args.filename_list,
                                transformations)
    train_loader = torch.utils.data.DataLoader(dataset=pose_dataset,
                                               batch_size=batch_size,
                                               shuffle=True,
                                               num_workers=2)
    model.cuda(gpu)
    softmax = nn.Softmax()
    criterion = nn.CrossEntropyLoss().cuda(gpu)
    reg_criterion = nn.MSELoss().cuda(gpu)
    # Regression loss coefficient
    alpha = args.alpha
    idx_tensor = [idx for idx in xrange(66)]
    idx_tensor = Variable(torch.FloatTensor(idx_tensor)).cuda(gpu)
    optimizer = torch.optim.Adam([{'params': get_ignored_params(model), 'lr': 0},
                                  {'params': get_non_ignored_params(model), 'lr': args.lr},
                                  {'params': get_fc_params(model), 'lr': args.lr * 2}],
                                   lr = args.lr)
    print 'Ready to train network.'
    print 'First phase of training.'
    for epoch in range(num_epochs):
        for i, (images, labels, name) in enumerate(train_loader):
            images = Variable(images.cuda(gpu))
            label_yaw = Variable(labels[:,0].cuda(gpu))
            label_pitch = Variable(labels[:,1].cuda(gpu))
            label_roll = Variable(labels[:,2].cuda(gpu))
            optimizer.zero_grad()
            model.zero_grad()
            pre_yaw, pre_pitch, pre_roll, angles = model(images)
            # Cross entropy loss
            loss_yaw = criterion(pre_yaw, label_yaw)
            loss_pitch = criterion(pre_pitch, label_pitch)
            loss_roll = criterion(pre_roll, label_roll)
            # MSE loss
            yaw_predicted = softmax(pre_yaw)
            pitch_predicted = softmax(pre_pitch)
            roll_predicted = softmax(pre_roll)
            yaw_predicted = torch.sum(yaw_predicted * idx_tensor, 1)
            pitch_predicted = torch.sum(pitch_predicted * idx_tensor, 1)
            roll_predicted = torch.sum(roll_predicted * idx_tensor, 1)
            loss_reg_yaw = reg_criterion(yaw_predicted, label_yaw.float())
            loss_reg_pitch = reg_criterion(pitch_predicted, label_pitch.float())
            loss_reg_roll = reg_criterion(roll_predicted, label_roll.float())
            # print yaw_predicted, label_yaw.float(), loss_reg_yaw
            # Total loss
            loss_yaw += alpha * loss_reg_yaw
            loss_pitch += alpha * loss_reg_pitch
            loss_roll += alpha * loss_reg_roll
            loss_seq = [loss_yaw, loss_pitch, loss_roll]
            # loss_seq = [loss_reg_yaw, loss_reg_pitch, loss_reg_roll]
            grad_seq = [torch.Tensor(1).cuda(gpu) for _ in range(len(loss_seq))]
            torch.autograd.backward(loss_seq, grad_seq)
            optimizer.step()
            if (i+1) % 100 == 0:
                print ('Epoch [%d/%d], Iter [%d/%d] Losses: Yaw %.4f, Pitch %.4f, Roll %.4f'
                       %(epoch+1, num_epochs, i+1, len(pose_dataset)//batch_size, loss_yaw.data[0], loss_pitch.data[0], loss_roll.data[0]))
                # if epoch == 0:
                #     torch.save(model.state_dict(),
                #     'output/snapshots/' + args.output_string + '_iter_'+ str(i+1) + '.pkl')
        # Save models at numbered epochs.
        if epoch % 1 == 0 and epoch < num_epochs:
            print 'Taking snapshot...'
            torch.save(model.state_dict(),
            'output/snapshots/' + args.output_string + '_epoch_'+ str(epoch+1) + '.pkl')
    print 'Second phase of training (finetuning layer).'
    for epoch in range(num_epochs_ft):
        for i, (images, labels, name) in enumerate(train_loader):
            images = Variable(images.cuda(gpu))
            label_yaw = Variable(labels[:,0].cuda(gpu))
            label_pitch = Variable(labels[:,1].cuda(gpu))
            label_roll = Variable(labels[:,2].cuda(gpu))
            label_angles = Variable(labels[:,:3].cuda(gpu))
            optimizer.zero_grad()
            model.zero_grad()
            pre_yaw, pre_pitch, pre_roll, angles = model(images)
            # Cross entropy loss
            loss_yaw = criterion(pre_yaw, label_yaw)
            loss_pitch = criterion(pre_pitch, label_pitch)
            loss_roll = criterion(pre_roll, label_roll)
            # MSE loss
            yaw_predicted = softmax(pre_yaw)
            pitch_predicted = softmax(pre_pitch)
            roll_predicted = softmax(pre_roll)
            yaw_predicted = torch.sum(yaw_predicted.data * idx_tensor, 1)
            pitch_predicted = torch.sum(pitch_predicted.data * idx_tensor, 1)
            roll_predicted = torch.sum(roll_predicted.data * idx_tensor, 1)
            loss_reg_yaw = reg_criterion(yaw_predicted, label_yaw.float())
            loss_reg_pitch = reg_criterion(pitch_predicted, label_pitch.float())
            loss_reg_roll = reg_criterion(roll_predicted, label_roll.float())
            # Total loss
            loss_yaw += alpha * loss_reg_yaw
            loss_pitch += alpha * loss_reg_pitch
            loss_roll += alpha * loss_reg_roll
            # Finetuning loss
            loss_angles = reg_criterion(angles[0], label_angles.float())
            loss_seq = [loss_yaw, loss_pitch, loss_roll, loss_angles]
            grad_seq = [torch.Tensor(1).cuda(gpu) for _ in range(len(loss_seq))]
            torch.autograd.backward(loss_seq, grad_seq)
            optimizer.step()
            if (i+1) % 100 == 0:
                print ('Epoch [%d/%d], Iter [%d/%d] Losses: pre-yaw %.4f, pre-pitch %.4f, pre-roll %.4f, finetuning %.4f'
                       %(epoch+1, num_epochs_ft, i+1, len(pose_dataset)//batch_size, loss_yaw.data[0], loss_pitch.data[0], loss_roll.data[0], loss_angles.data[0]))
                # if epoch == 0:
                #     torch.save(model.state_dict(),
                #     'output/snapshots/' + args.output_string + '_iter_'+ str(i+1) + '.pkl')
        # Save models at numbered epochs.
        if epoch % 1 == 0 and epoch < num_epochs_ft - 1:
            print 'Taking snapshot...'
            torch.save(model.state_dict(),
            'output/snapshots/' + args.output_string + '_epoch_'+ str(num_epochs+epoch+1) + '.pkl')
    # Save the final Trained Model
    torch.save(model.state_dict(), 'output/snapshots/' + args.output_string + '_epoch_' + str(num_epochs+epoch+1) + '.pkl')
code/train_preangles.py
@@ -134,7 +134,7 @@
    criterion = nn.CrossEntropyLoss().cuda()
    reg_criterion = nn.MSELoss().cuda()
    # Regression loss coefficient
    alpha = 0.00
    alpha = args.alpha
    idx_tensor = [idx for idx in xrange(66)]
    idx_tensor = Variable(torch.FloatTensor(idx_tensor)).cuda(gpu)