-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
s-image.c: fix offsets when inputting an image. #54
Conversation
If I only had such a thing. As far as I can tell, previous development model was to never make errors and thus no tests are needed. (Or more seriously, many things are hard to test, because it's (a) Monte-Carlo, and (b) the truth is often not known, since we can't asssume CIAO to be correct and static either. So, tests are mostly "does it run without a crash?" and "Does it look fine by eye?". In other words, I'll have to look at this is detail because I can't rely on tests telling me if this is the correct way to change things. |
That's unfortunate 😞 ... but I'm also familiar with that modus operandi. Let me know if you need more info. FWIW: to debug I cut the input image down to a 5x5 pixel image so that it was easier to see exactly which pixel was being used. dmcopy "delta.img[sky=box(4097,4097,5,5)]" 5x5.img |
There are a couple of fixes here - The binary_search routine is finding the last pixel below the random value. However, what is needed is the 1st pixel that includes (>=) the random value. This is causing a 1 pixel shift in X (or a missed wrap if at the edge of the image). - We want to use `floor(x/len)` when computing the X coordinate. Even though this is integer divided by integer, it's still producing a floating point value -- but we don't want the fractional part of division. This will also cause a shift in the X coordinate. The other `+1` changes are there to help clarify the change from C-index (0:n-1) to pixel index (1:n).
Do we know if this will make if doe the December 2023 release? |
Yes, it will. |
I usually prepare the next marx release when I have the new version of the CALDB, because the contamination files get reformated and included into the marx release. |
So, with this fix, it moves the center of the simulated distribution from (4096, 4097) to (4097.5, 4097.5). I thought that in Chandra, the pixel center is an integer number, so it should have been moved to (4097.0, 4097.0), right? So, maybe
should be changed to
These questions on "is the corner (0,0) or (1,1) or (0.5, 0.5)" always confuse me... |
I should say that I get "the center" as
because I'm afraid I'm shooting myself in the foot when I do:
Or is looking at the X,Y in the event file not valid and it only becomes OK when I look at the binned image? |
I don't like comparing centroids computed from images w/ those computed from events for this kind of reason ; with image the weight is all at the center of the pixel, with events it's {whatever} (to within the pixel boundaries). So they're really not the same. It's been a while since I've looked, but the +1 there is, I think, just going from C-arrays starting at 0, and the WCS defining the low-left pixel pixel as (1,1). The bin edge really depends on the bin syntax, so in this example the 4046.5:4146.5:1, the 1st pixel includes x values from 4046.5 up to but not including 4047.5, so 4047 would be the middle of the pixel. But if the bin was 4046:4146:1, then the xcenter of the 1st pixel would be 4047.5. |
Thanks. I'll figure it out ;-) Note: I renamed branches in the repro for unrelated reasons after I used a bunch of cherry-picking to back out (but keep in a separate branch) some commits I don't want to release yet. When I reset this PR to go onto the branch that's currently called "main" (but had a different name when this PR was opened), all of those cherry-picked commits get added to this PR. Thus, I've manually taken this one commit (by using another cherry-pick!) and put int locally onto my main; I'll either update this PR later or merge locally and push directly to main. |
ofs = Image_Size-1; | ||
} | ||
|
||
y = (double) floor(ofs / X_Image_Size); // floor() because we want the integer part only here |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
y = (double) floor(ofs / X_Image_Size); // floor() because we want the integer part only here | |
y = (double) (ofs / X_Image_Size); |
Non need for "floor" - /
is integer division in C, the result will always be the integer part, it's not going to do any hamr iether though: https://stackoverflow.com/questions/12240228/c-integer-division-and-floor
// binary_search is finding the last pixel less than JDMrandom(). | ||
// We want the 1st pixel that includes JDMrandom(), so +1. | ||
ofs = JDMbinary_search_f (JDMrandom (), Image, Image_Size) +1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// binary_search is finding the last pixel less than JDMrandom(). | |
// We want the 1st pixel that includes JDMrandom(), so +1. | |
ofs = JDMbinary_search_f (JDMrandom (), Image, Image_Size) +1; | |
ofs = JDMbinary_search_f (JDMrandom (), Image, Image_Size); |
The comment "last pixel less than" I think is based on a misunderstanding of the indexing scheme. It's easier to think of this a zero-indexed array - and it returns the index into a zero-indexed array.
if (ofs >= Image_Size) { | ||
ofs = Image_Size-1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (ofs >= Image_Size) { | |
ofs = Image_Size-1; | |
} |
Once an interval was narrowed down to just two elements, the binary search algorithm always returned the lower of the two. That's not correct. This bug was found by simulations of a one-pixel source that happens to be off by one pixel. If, in that example, the source would have been located at +1 or -1 pixel compared to the test that was run, this bug would have gone unnoticed. Changes in "s-image.c" are mostly cosmetic to make it easier to reason why the formula works, by removing the double negative. I *think* there was als oa 0.5 pixel offset before, but since it's hard to reason about, I opted for simplifying and making sure it's correct by testing a simulation. fixes Chandra-MARX#50 closes Chandra-MARX#54
It turns out that this is not a problem from the image source, but instead there was an underlying bug in the binary search in JDmath, that's been there for at least two decades and also effects other parts of marx - for example when selecting with energy bin to put a photon in a CCD level ARF. About 50% of the time, the result of the binary search would be off by 1. Fortunately, our ARF binning typically oversamples so it's not a big impact in practice, but it's annoying nevertheless and shows why one should use well-tested standard libraries instead of implementing your own math functions! I'm also adding tests based on the example in #50 to the marx test suite which is slowly growing now that I've found a way to use a single framwork (pytest) to drive both tests of the functions C code itself (through CFFI) and end-to-end tests that involve CIAO tools (through Thank you so much for @kglotfelty for your original investigation and I apologize that it's taken me so long to take the time to get to the bottom of this and fix it. |
This is for #50
There are a couple of fixes here
binary_search
routine is finding the last pixel below the random value. However, what is needed is the 1st pixel that includes (>=) the random value. This is causing a 1 pixel shift in X (or a missed wrap if at the edge of the image).floor(x/len)
when computing the X coordinate. Even though this is integer divided by integer, it's still producing a floating point value -- but we don't want the fractional part of division. This will also cause a fractional shift in the X coordinate.The other
+1
changes are there to help clarify the change from C-index (0:n-1) to pixel index (1:n).This looks to fix the case in #50, though it's easier to see when working with an image with odd axis lengths
dmcopy delta.img"[bin #1=1:101:1,#2=1:101:1]" delta101.img pset marx_delta S-ImageFile=delta101.img
Will definitely need to be run through marx's test suite.