Pixel-for-pixel image display not working

Hi Chris,

I am trying to generate an image that will be displayed pixel-for-pixel correct on the MWorks display screen. I am seeing artifacts that look like image compression artifacts.

I am using a recently nightly: MWServer reports 0.4.5-dev (1f3634b).
My display is 1280x1024. I generate a 1280x1024 png image.
I’m attaching both the image and a reduced XML example experiment.
First, I set x_size and y_size to the full range of the display as reported by the server.

00:03:17: Display bounds set to (-35.9421 left, 35.9421 right, 28.7918 top, -28.7918 bottom)

I set x0 and y0 to 71.842, 57.584

Issue #1: the image does not span the entire y range, though it appears to span the entire x range of the screen.
Issue #2 (the important one!): the image displayed is not pixel-by-pixel correct and I cannot get it to be so by tweaking the size. The image has alternating vertical bars, one pixel width that are repeated cycles of (black, gray, white, gray). What I see on the screen instead has bars of greater than one pixel width, and there is low-frequency structure. The image appearance is similar to what I get if I use Photoshop or Preview to rescale the image.

Any idea how I can generate an image that will just be displayed on the screen as is?

Best
Mark

Attachments:

Hi Mark,

I haven’t had a chance to look in to this in detail, but my initial guess is that pixel-for-pixel accuracy will be hard (maybe impossible) to achieve with the existing image stimulus.

The issue is that each image is converted into a set of OpenGL textures (“mipmapped” in GL parlance), which are then used in rendering to the specified display area. In general, this is the right way to do it, since it maximizes both output quality and flexibility in position and sizing. However, it also means that some scaling is probably happening, which could be the source of the visual artifacts you’ve observed.

Of course, so far I’m only guessing, so I’ll need to do some tests to verify my theory. If I’m correct, the right solution for your problem may be to implement a simpler, “raw pixels” image stimulus that just draws the image pixels on screen without any scaling.

I’ll get back to you when I have more info.

Chris

Hi Chris,
Thanks. Let me know what you find. I think I have a way to work around this with the dynamic grating stimulus, so this is not high priority.

Just so it doesn’t get lost in the middle of my message above – I believe it’s still true that setting the y_size to the calculated y size of the display produces an image that does not fill the screen. This appears to also be true for the dynamic grating.

Best
Mark

Hi Mark,

With regard to pixel-for-pixel image display, my initial guess was correct. As described in the man page for gluBuild2DMipmaps, the first step in generating mipmap levels is to scale the image so that its width and height are both powers of 2. In your case, the image would be scaled to 1024x1024. I’ve attached a copy of your image scaled to those dimensions. When viewed full size, it exhibits artifacts similar to what you’ve observed.

As I’ve said already, the right solution here is to create a new “unscaled image” stimulus that draws images pixel-for-pixel. This would be pretty straightforward. Do you want me to go ahead with it?

The reason why your image doesn’t fill the screen vertically is a bit obscure. When an image is drawn, the rectangle defined by x_size/y_size is scaled into a square. The image is then rendered so that its aspect ratio is preserved within this scaled coordinate system. If you want the image’s aspect ratio to be preserved onscreen, then x_size/y_size must be equal. (Presumably there’s a rationale behind this design, but I can’t guess what it is.)

So, if you want your 1280x1024 image to fill your 1280x1024 screen, you should set both x_size and y_size to the width of the display as reported by MWorks (71.8842 on your setup).

As for the drifting grating, in my tests it doesn’t fill the screen horizontally or vertically. I need to investigate more to figure out why.

Chris

Attachment: generated-0-scaled.png (3.17 KB)

(Presumably there’s a rationale behind this design, but I can’t guess what it is.)

This is so that users can put up undistorted stimuli, without having to keep track of the aspect ratio of each and every stimulus (which would require correctly calculating the width/height each time they changed them. This would be error prone and confusing). There could very well be a better way to factor this (e.g. width + horiztonal stretch factor?), but it can get a bit complicated, since many “generated” stimuli really are meant to fill up whatever box you tell them to, so width and height are appropriate degrees of freedom.

Dave

As for the drifting grating, in my tests it doesn’t fill the screen horizontally or vertically. I need to investigate more to figure out why.

I figured this one out, too: All of the currently available grating masks are implemented as 128x128 pixel grids. The inner 127x127 pixels are determined by the mask type, but the edge pixels are always zero luminance, zero alpha. Thus, when you set the dimensions of the grating to match those of the display, the grating does take up the full display, but the edges are fully transparent, so it looks like it doesn’t quite fill the space. As with the image drawing code, the grating clearly was designed to work this way, but I don’t know why.

If you want the grating to fill the screen, you need to make its dimensions a bit larger than the display dimensions reported by MWorks.

Chris

That sounds like a plain-and-simple bug. If we’re talking about the
same Drifting Grating plugin, there were a number of things funky about
it when we first “found” it, so I wouldn’t be surprised if this were
simply a math error.

I’d rather see it get fixed than have people start kludging around odd
behavior. Also, in a perfect world at least, the grating mask size probably
ought to be user-settable.

  • Dave

Thanks.
I used the ‘rectangle’ grating. As far as I can tell it does not have a size parameter, but I didn’t look extremely hard.

Best,
Mark

That sounds like a plain-and-simple bug.

I don’t think it is. The drawing code clearly makes use of those border pixels in order to produce the requested aspect ratio, so they’re definitely intentional. But that’s not to say that it’s a good way to do things, and the resulting behavior is indeed odd and undesirable. I’ll try to figure out a better way to do it.

Also, in a perfect world at least, the grating mask size probably ought to be user-settable.

Agreed. We should add that, too.

Chris