If you negate the input image then the gradients will have opposite directions (G <- -G
).
You need to remind that SIFT descriptors are basically histogram of gradient orientations:
Since the gradient is negated on the inverted image we obtain:
0th arrow => 4th arrow
1st arrow => 5th arrow
2nd arrow => 6th arrow
3th arrow => 7th arrow
In other words if you consider the first 8-bins histogram (there are 4x4 such histograms in total), and if you denote a
, b
, etc the related SIFT descriptors components, we have:
- original image:
[a, b, c, d, e, f, g, h]
- inverted image:
[e, f, g, h, a, b, c, d]
So you can convert the inverted image SIFT descriptor by swapping the components by 4-sized packs.
Pseudo-algorithm:
# `sift` is the 128-sized array that represents the descriptor
NCELLS = 16
NORI = 8
0.upto(NCELLS - 1) do |cell|
offset = cell * NORI
offset.upto(offset + NORI/2 - 1) do |i|
sift.swap!(i, i + NORI/2)
end
end
Here's how to verify this with vlfeat:
- Negate the default image:
convert -negate default.pgm negate.pgm
- Extract keypoints on default image:
./sift --frames default.pgm
- Select the first keypoint:
tail -n 1 default.frame > kpt.frame
- Describe it with the default image:
./sift --descriptors --read-frames kpt.frame default.pgm
- Describe it with the negated image:
./sift --descriptors --read-frames kpt.frame negate.pgm
- Format both descriptors with 4 components per line (see below)
Then visualize the output with e.g. diff -u
or opendiff
: the lines are swapped 2-by-2 as expected.
cat default.descr | ruby -e\
'STDIN.read.split(" ").each_slice(4) {|s| p s}'\
> default.out
cat negate.descr | ruby -e\
'STDIN.read.split(" ").each_slice(4) {|s| p s}'\
> negate.out