The solution is:
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/p5ENg.png)
Step 1:
Begin by extending lines through white circles on the edge, and radiating in from black circles on the edge. Black circles in corners can be extended in both directions.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/9lRKS.png)
The 9 in the top-left can be fully resolved, terminating a single dot away from the white 3 below it and the black 2 to its right (otherwise these smaller numbers would be violated). The 2 and 3 can then also be fully resolved, as can the white 3 in row 7. We can also place at least 3 line segments extending through the white 4 in row 2.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/llgCw.png)
Now the black 5 at the end of row 3 can only extend at most 3 segments to the left (else it meets the line from the white 3, violating its constraint). It must therefore extend at least 2 segments down, since up is not an option (the line will end up trapped in a corner). But this means that the line snaking from the 2 in row 1 must extend down 1 unit further, meeting the line from the 5 and setting its length to 1. Thus we must have 4 units of line extending down from the 5.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/UnaqF.png)
This means that the black 3 at the end of row 9 can extend vertically at most 1 unit and must therefore extend 2 units to the left.
Meanwhile, the line through the white 7 on the bottom row can also be extended at least 3 spaces to the left (since only room for 4 on its right), and at least 3 to the right (since cannot meet the line coming from the 3 bottom-left). The black 3 above it can be partially resolved.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/lkoDc.png)
Step 2:
Note that the 2 at the end of row 10 can now only be resolved one way. This then resolves the white 7, the two black 3's in the bottom left, then the other numbers in the bottom-left corner section also.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/h5E9r.png)
Step 3:
Next, note that the white 5 cannot be satisfied horizontally, or the line extends through the nearby 4 and violates its constraint. It must therefore be crossed vertically instead. To avoid violating the black 2 below it, there is only one way to satisfy it. This forces the 2 to link to the nearby black 3 and also finalises the loop passing through the nearby 4, with knock-on deductions resulting in the top-right section being fully resolved.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/tjh5e.png)
Step 4:
The black 3 in line 3 must now have a line extending 2 spaces to the left (it can only extend 1 either up or down). This then forces the resolution of the white 4 in row 2 and has some knock-on effects for the black 3 and its neighbouring black 5.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/g4oel.png)
Now the white 4 in row 5 can only be satisfied horizontally.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/7t546.png)
Step 5:
Now an interesting logical step: Consider what would happen if the black 5 were to be satisfied by extending the line downwards - we end up in an impossible position which separates the top and bottom of the grid into separate loops:
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/z70jR.png)
Thus the 5 must be completed using the line to the left instead.
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/vkqQp.png)
Step 6:
The 4 loose points in the centre must all connect up to each other to avoid dead ends. Thus the rest of the grid can be resolved:
![enter image description here](https://cdn.statically.io/img/i.sstatic.net/d4tR8.png)
And now there is only one way to lay the last segments to keep one single loop!