
The Center of the Pixel is (0.5, 0.5) - ingve
http://www.realtimerendering.com/blog/the-center-of-the-pixel-is-0-50-5/
======
alleycat5000
This comes up all the time in dealing with geospatial rasters.

For instance, in GDAL there's a whole RFC for dealing with issues related to
pixel corners versus pixel centers!

[https://gdal.org/development/rfc/rfc33_gtiff_pixelispoint.ht...](https://gdal.org/development/rfc/rfc33_gtiff_pixelispoint.html)

"Traditionally GDAL has treated this flag as having no relevance to the
georeferencing of the image despite disputes from a variety of other software
developers and data producers. This was based on the authors interpretation of
something said once by the GeoTIFF author. However, a recent review of section
[section 2.5.2.2] of the GeoTIFF specificaiton has made it clear that GDAL
behavior is incorrect and that PixelIsPoint georeferencing needs to be offset
by a half a pixel when transformed to the GDAL georeferencing model."

------
__abadams__
Placing pixel centers at 0.5, 0.5 is only the obvious choice if you think
pixels are little squares rather than point samples. Pixels-as-squares makes
intuitive sense to people, especially those raised on pixel art, but it's just
one possible choice you can make for your reconstruction function. It's not
even a particularly good choice. It doesn't model real sensors or real
displays, and it doesn't have particularly nice theoretical properties. The
only thing going for it is that it's cheaper to compute some things.

~~~
anderskaseorg
It’s true that pixels aren’t most accurately modeled as squares, but they
should still be centered at (0.5, 0.5), because you want the center of mass of
a W×H pixel image to be at exactly (W/2, H/2) no matter what shape the pixels
are. Otherwise it shifts around when you resize the image—perhaps even by much
more than 1 pixel if you resize it by a large factor.

~~~
0xfaded
Unfortunately doesn't make sense when you need to look up pixel 0.5,0.5 in the
framebuffer.

When dealing with cameras, the central point us rarely h/2,w/2\. So you're
really dealing with two sets of coordinates, camera coordinates and sensor
coordinates, that need to be converted between.

Integer coordinates are convenient for accessing the sensor pixels, and the
camera-to-sensor space transform should theoretically include for the 0.5,0.5
offset. However, getting a calibration within 0.5 pixels accuracy is going to
be hard to begin with.

~~~
anderskaseorg
Nobody’s suggesting that pixels are stored at half-integer memory addresses.
After all, only a small subset of the continuous image space will lie exactly
on the grid of pixel centers—and this is true no matter how the grid is
offset. The point is that the grid should be considered as being lined up with
(0.5, 0.5) rather than with (0, 0).

So, for example, if you’re scaling an image up by 10× with bilinear
interpolation, and you need to figure out what to store at address (7, 23) in
the output framebuffer, you should convert that to continuous coordinates
(7.5, 23.5), scale these continuous coordinates down to (0.75, 2.35), and use
that to take the appropriate weighted average of the surrounding input pixels
centered at (0.5, 1.5), (1.5, 1.5), (0.5, 2.5), and (1.5, 2.5), which are
located at address (0, 1), (1, 1), (0, 2), and (1, 2) in the input
framebuffer. The result will be different and visually more correct than if
you had done the computation without taking the (0.5, 0.5) offset into
account. In this case the naive computation would instead give you a
combination of the pixels at (0, 2), (1, 2), (0, 3), and (1, 3) in the input
framebuffer, and the result would appear to be shifted by a subpixel offset.
This was essentially the cause of a GIMP bug that I reported in 2009:
[https://bugzilla.gnome.org/show_bug.cgi?id=592628](https://bugzilla.gnome.org/show_bug.cgi?id=592628).

------
LeoPanthera
Related: "A pixel is not a little square".

[http://alvyray.com/Memos/CG/Microsoft/6_pixel.pdf](http://alvyray.com/Memos/CG/Microsoft/6_pixel.pdf)

~~~
pornel
However, image is not a wave either.

I have mixed feelings about this memo. It's right about practical aspects of
resampling filters, but tries too hard to justify that with sampling theory.
For example, pixel-aligned sharp edges exist and are meaningful in images,
unlike perfectly square waves in sampling theory.

~~~
sudosysgen
Pixel aligned sharp edges do not actually exist and are not meaningful in
images, because a perfectly sharp lens does not exist (and cannot exist) and
as a result you can never form a sharp edge in an image. You also have de-
focus that prevents you from doing so, and a lens that has a wider depth of
field has an _immediately_ noticeable limit in sharpness.

Even if you were somehow able to create a perfect lens, you would not be able
to create a perfectly sharp edge with real world objects.

~~~
wtallis
See, here you're committing the same error that the paper does: pretending
pixels are all about photography and optics, and ignoring that some computer-
generated graphics actually are supposed to represent perfect squares.

Sometimes, pixels really are little squares. Not always, but not never,
either.

~~~
chaorace
I disagree. Would you say a specific address in an array has edges? No,
because it's an address, a fixed point. Even if that array accidentally
described a square, the individual array addresses would have nothing to do
with that.

It's not a question of the representation, it's a question of quanta. Pixels
are data, the little squares are the artifacts your LCD eventually produces
with the help of that data.

~~~
wtallis
When your GPU is rasterizing the edges of polygons, it computes (sometimes
just approximates) how much of a little square is covered by that polygon and
uses that as the weight when averaging what color to assign to that pixel. The
resulting rendered image is most correctly interpreted as an array of little
squares, not point samples and definitely not truncated gaussians.

~~~
sudosysgen
Actually no, that's not the case, rasterization is on-off at the hardware
level. You need anti-aliasing for the behaviour you are describing, which very
rarely works the way you describe - the best we have right now as far as
quality uses multisampling.

~~~
Jasper_
2D graphics uses coverage-based antialiasing which computes the coverage over
the pixel square.
[https://nothings.org/gamedev/rasterize/](https://nothings.org/gamedev/rasterize/)

~~~
sudosysgen
And that is not done by the GPU. The top comment started with "your GPU
does...."

In any case, that is an extreme edge case of software renderers that doesn't
even come close to a significant part of 2D graphics in real life. Indeed,
most 2D graphics is really flat 3D graphics done using GPU routines and does
not the work that way. I know that some extreme edge cases do use coverage
based rasterization, but :

>You need anti-aliasing for the behaviour you are describing, which very
rarely works the way you describe

This is a case of anti-aliasing (read the title of the article) and is
extremely rarely used. It's essentially irrelevant when discussing how
graphics work in real life.

I really cannot overstate just how rarely software rasterizers are used for
interactive graphics in 2020, coverage based rasterizers are an even smaller
subset of that. It really makes a ton more sense to use a GPU rasterizer and
use MSAA or oversample the whole image.

~~~
Jasper_
2D graphics on the GPU is an open research problem. My understanding is that
piet-gpu and Pathfinder, both state of the art research projects, use
coverage-based solutions based on the GPU. MSAAx16, which is incredibly
expensive on mobile devices, only provides 16 different coverage values, and
from my limited tests was poorer quality than a coverage-based solution.

~~~
sudosysgen
2D graphics on the GPU is not an open research problem in practice. In real
life you either use Direct2D or OpenGL/Vulkan/Direct3D and just... ignore a
dimension.

Yes, MSAA 16x is incredibly expensive on mobile devices, and it provides a
worse result than a coverage based approach. But MSAA 16x is done by _an asic_
, and is simpler than coverage based AA. It is not even _close_ in
performance. A GPU ROP _trounces_ any programmable compute unit as far as
performance, it's not even closed. It is done by pecialized, in silico
hardware. And in practice MSAA 8x is more than good enough, _especially_ on
mobile devices. You certainly will not notice a difference on a phone with a
density of _563_ dpi between MSAA 4x and 8x, let alone 16x and coverage based.

At those scales, the resolution of the phone is literally greater than the
optical resolution of the optical system that is your eyes. There is no point
in anything beyond MSAA 4x in reality, and a lot of people with displays in
the 200 dpi range just use 2X MSAA while they could use 8X MSAA because they
really can't tell the difference.

The final nail in the coffin is that these compute-based rasterization engines
so far more or less match the performance of _CPU_ rasterization. This is
simply unacceptable when GPU direct rasterization can give results nearly
indistinguishable at multiple times the performance and much less power usage.
This is literally taking something done by a highly optimized, 12-7nm ASIC,
and trying to do it through compute for a tiny improvement. It's absurd.

------
mark-r
I once made a persuasive argument that this was the proper interpretation of a
pixel, and got a major app to adopt the convention. It wasn't until much later
that I discovered the error.

The problem comes when you try to align raster images with vector ones.
Instead of starting a line at 0,0 you need to start it at 0.5,0.5. And heaven
help you if you're combining raster images at different resolutions, they'll
never line up.

The proper way to work is to put the pixel center at 0,0 and let it extend
from -0.5 to 0.5. This works out well with reconstruction or resizing filters,
because they're symmetric around 0,0 too.

~~~
codeflo
Pixel centers at (0.5, 0.5) is the convention adopted by all major graphics
APIs (OpenGL, DirectX, Vulkan, Metal). That’s because it makes the math,
especially around image scaling, becomes a lot simpler. What exactly the
alignment problem you say isn’t solvable?

One common mistake is to not think in floating point coordinates all the way
through. E.g. a rectangle that covers a single pixel should have coordinates
like (100.0,100.0)-(101.0,101.0), NOT involving a 0.5 offset. You almost never
offset anything by 0.5 in this convention. 1 pixel wide lines are an
exception, but only because then the edges of the line are exactly at pixel
boundaries.

------
Kednicma
I know that many languages have some sort of support for units. It would be
nice to have libraries which explicitly say that (0,1) and (-1,1) are
different, and support transforming between them. I think that this
transformation comes up all the time when working with pixels that are
properly aligned and centered.

~~~
aspaceman
I know of yt-project, which has a lot of cool support for units in the context
of sci-vis. Support for transforms between coordinate systems is nice though.
Would love to have that. The only hard part is that systems which try and do
this sort of thing lose some of the elegance of saying "V = (0, 1)" when you
also have to specify the coordinate system you're working under for every
vector.

There have been some papers that do this though. I can't find the reference
but I know it exists.

------
redis_mlc
My understanding is that graphics are smeared on Unix systems, but sharp on
Windows systems, because Unix libraries use 0.5, 0.5 as the center and Windows
uses 0,0.

Can anybody confirm that they investigated that deeply and found that to be
true, or is there another explanation?

~~~
codeflo
I have never heard such a thing. Are you talking about font rendering
differences? There are different anti-aliasing algorithms, but they don’t have
anything to do with this convention.

------
fallingfrog
I once made a half-pixel error trying to write a fast Fourier transform as a
series of fragment shaders. That was nasty to track down! The error was
smeared across all the pixels in the frequency domain causing weird subtle
frequency biases in certain directions.

------
qubex
Agree.

Hard to disagree, really.

~~~
TwoBit
I wonder what drove Microsoft to fo it differently (and wrongly), despite that
GL had already solved this.

------
VikingCoder
Yes, and enjoy working with MIPMaps.

------
ur-whale
Not exactly news

