Yes, the backward call and the optimizer are supposed to update your input image.
However, one condition on this is, that you don’t detach the computation graph.
This happens e.g. if you use some numpy operations on your data, which autograd cannot trace. Also, this happens if you are re-wrapping variables as it seems to be the case for your loss.
Currently you are using loss = Variable(total_loss.data, requires_grad=True), which cuts the graph right at the end so that no gradients will be calculated.
Have a look at this small code example:
model = nn.Linear(10, 2)
x = torch.randn(1, 10, requires_grad=True)
y = torch.randn(1, 2)

output = model(x)
loss = ((output - y)**2).mean()
loss.backward()
print(model.weight.grad)
print(x.grad)

# Now let's re-wrap the loss
model = nn.Linear(10, 2)
x = torch.randn(1, 10, requires_grad=True)
y = torch.randn(1, 2)

output = model(x)
loss = ((output - y)**2).mean()
det_loss = torch.tensor(loss.data, requires_grad=True)
det_loss.backward()
print(model.weight.grad)
print(x.grad)

Could you try to fix this and then check the gradients in your input image?
Also as a small side note, Variables and tensors were merged in 0.4.0, so you should update to the current stable or preview version. You will find install instructions here 2.