Opacity correction in Raycasting Volume Rendering - opengl

I want to implement high-quality raycasting volume rendering using OpenGL、GLSL and C++. And I use image-order volume rendering. During a step of raycasting volume rendering called compositing, I use the following formula (front-back-order):
When I read the book 《Real-time volume graphics》 page 16,I see that we need to do Opacity Correction if sample rate changes:
And use this opacity value to replace the old opacity value.In this formula,
is the new sample distance,and the △x is the old sample distance.
My question is : How do I determine △x in my program?

Say your original volume had a resolution of V=(512, 256, 127) then when casting a ray in the direction of (rx, ry, rz), the sample distance is 1/|r·V|. However say in your raycaster you're largely oversampling, say you sample the volume at 3× the sample distance V'=(1526, 768, 384), the oversampled sample distance is 1/|r·V'|, and hence the ratio of sampling rates is 1/3. This is the exponent to add.
Note that the exponent is only noticeable with low density volumes, i.e. in the case of medical images low contrast soft tissue. If you're imaging bones or dense tissue then it makes almost no difference (BTDT).

datenwolf is correct, but there is one piece of missing information. A transfer function is a mapping between scalar values and colors. In RGBA, the range of values is between 0 and 1. You, as the implementer, get to choose what the actual value of opacity translates to, and that is where the original sample distance comes in.
Say you have the unit cube, and you choose 2 samples that translates to a sample distance of 0.5, if you trace a ray in a direction orthogonal to a face. If the alpha value is 0.1 everywhere in the volume, the resulting alpha is 0.1 + 0.1 * (1.0 -0.1) = 0.19. If you chose 3 samples, then the resulting alpha is one more composite from the previous choice: 0.19 + 0.1 * (1-0.19) = 0.271. Thus, your choice of the original sample distance influences the outcome. Chose wisely.

Related

interpolation for smooth downscale of image in OpenCV

I noticed that of the two methods below for scaling an image N halfs that the first produced a more smooth image, looking more appealing to the eye.
while (lod-- > Payload->MaxZoom)
{
cv::resize(img, img, cv::Size(), 0.5, 0.5, cv::INTER_LINEAR);
}
vs
double scale = 1.0 / (1<< (lod - Payload->MaxZoom));
cv::resize(img, img, cv::Size(), scale, scale, cv::INTER_LINEAR);
I am interested in knowing if there is a interpolation that would produce similar result as the first resize but not having to loop over it N times.
Any mathematical insight into why doing the resize in multiply steps can result in a better result is also interesting.
The latter method above gives a very pixelated result (for N=5) where the first is very smooth (it makes sense since its the average of 4 pixel over N steps)
This happens because OpenCV's implementation of linear interpolation is rather simplistic.
A simple implementation of linear interpolation takes the values of four pixels closest to the interpolated point and interpolates between them. This is all right for upscaling, but for downscaling, this will ignore the values of many pixels - if there are N pixels in the output image, then it depends on at most 4N pixels of the input. This cannot give good results when the product of scaling factors is lower than 0.25.
The correct thing to do is to consider all input pixels that correspond to an output pixel after the transformation, and compute an average over them (or more generally, compute a convolution with a suitable resampling filter).
OpenCV seems to have an interpolation mode called cv::INTER_AREA, which should do the thing you want.

OpenGL: Gamma corrected image doesn't appear linear

I'm using OpenGL for rendering, and when I write linear values to the default framebuffer (without any gamma correction) they appear linear on my monitor. This goes against everything I thought I knew about gamma correction (as explained here: http://gamedevelopment.tutsplus.com/articles/gamma-correction-and-why-it-matters--gamedev-14466 ). Without gamma correction, I would expect to see mid-range colors darkened non-linearly by my monitor.
But here is what I actually see; first with no gamma correction on my part, then with gamma correction:
Here's my fragment shader without gamma correction (drawn on a fullscreen quad to the default framebuffer). This results in the linear image on the left:
out vec4 fsOut0;
void main( void )
{
// split the screen into 10 discrete color bands
float yResolution = 768.0;
int intVal = int(gl_FragCoord.y / yResolution * 10.0);
fsOut0.rgb = vec3( float(intVal) / 10.0 );
fsOut0.a = 1.0;
}
And here's the shader with added gamma correction (from linear space to sRGB). This results in the brighter-than-linear image on the right:
out vec4 fsOut0;
void main( void )
{
// split the screen into 10 discrete color bands
float yResolution = 768.0;
int intVal = int(gl_FragCoord.y / yResolution * 10.0);
fsOut0.rgb = vec3( float(intVal) / 10.0 );
// gamma correction
fsOut0.rgb = pow( fsOut0.rgb, vec3(1.0/2.2) );
fsOut0.a = 1.0;
}
I'm verifying whether or not the colors are linear just by looking at them, and by using the color picker in Photoshop and looking at the differences in RGB values between color bands. For the linear-looking image the difference between each color is (mostly) constant.
I have also tried requesting an sRGB-capable default framebuffer. In this case, writing linear values with no gamma correction looks like the second image (non-linear).
What am I doing wrong? Or could it be that my two monitors are both miscalibrated AND that Photoshop does not pick colors in linear space? Or is my "non-linear" image actually the correct linear result, but it just doesn't seem linear to my eyes?
My question is sort of a duplicate of this: Do I need to gamma correct the final color output on a modern computer/monitor
Unfortunately the accepted answer is extremely confusing and the parts of it I was able to follow seem contradictory, or at least not fully explained for someone less knowledgeable than the answerer.
Well, both your left and right pictures are as is to be expected. They are perfectly fine, and yes, I know my stuff.
It's just that our eyes are not very linear either, so e.g. 1/5th of linear intensity (luminous intensity) is perceived as "half as bright as white". This is what you see on the right, in the corrected image, near the bottom.
This is the reason for gamma being there in the first place - to help encoding by mimicking the eye's response. IOW, gamma makes the non-linear ramp look linear.
However, a physically linear ramp (as on the right) is therefore quite counter to being perceived as linear. Remember that the real world has a quite large dynamic range (in terms of luminous intensity), and our eyes are compensating for that. This is confusing you, but unlike many others, you actually got the numbers right.

Determine difference in stops between images with no EXIF data

I have a set of images of the same scene but shot with different exposures. These images have no EXIF data so there is no way to extract useful info like f-stop, shutter speed etc.
What I'm trying to do is to determine the difference in stops between the images i.e. Image1 is +1.3 stops of Image0.
My current approach is to first calculate luminance from the image's RGB values using the equation
L = 0.2126 * R + 0.7152 * G + 0.0722 * B
I've seen different numbers being used in the equation but generally it should not affect the end result L too much.
After that I derive the log-average luminance of the image.
exp(avg of log(luminance of image))
But somehow the log-avg luminance doesn't seem to give much indication on exposure difference btw the images.
Any ideas on how to determine exposure difference?
edit: on c/c++
You have to generally solve two problems:
1. Linearize your image data
(In case it's not obvious what is meant: two times more light collected by your pixel shall result in two times the intensity value in your linearized image.)
Your image input might be (sufficiently) linearized already -> you may skip to part 2. If your content came from a camera and it's a JPEG, then this will most certainly not be the case.
The real 'solution' to this problem is finding the camera response function, which you want to invert and apply to your image data to get linear intensity values. This is by no means a trivial task. The EMoR model is widely used in all sorts of software (Photoshop, PTGui, Photomatix, etc.) to describe camera response functions. Some open source software solving this problem (but using a different model iirc) is PFScalibrate.
Having that said, you may get away with a simple inverse gamma application. A rough 'gestimation' for the right gamma value might be found by doing this:
capture an evenly lit, static scene with two exposure times e and e/2
apply a couple of inverse gamma transforms (e.g. for 1.8 to 2.4 in 0.1 steps) on both images
multiply all the short exposure images with 2.0 and subtract them from the respective long exposure images
pick the gamma that lead to the smallest overall difference
2. Find the actual difference of irradiation in stops, i.e. log2(scale factor)
Presuming the scene was static (no moving objects or camera), this is relatively easy:
sum1 = sum2 = 0
foreach pixel pair (p1,p2) from the two images:
if p1 or p2 is close to 0 or 255:
skip this pair
sum1 += p1 and sum2 += p2
return log2(sum1 / sum2)
On large images this will certainly work just as well and a lot faster if you sub-sample the images.
If the camera was static but the scene was not (moving objects), this starts to work less well. I produced acceptable results in this case by simply repeating the above procedure several times and use the output of the previous run as an estimate for the correct scale factor and then discard pixel pairs who's quotient is too far away from the current estimate. So basically replacing the above if line with the following:
if <see above> or if abs(log2(p1/p2) - estimate) > 0.5:
I'd stop the repetition after a fixed number of iterations or if two consecutive estimates are sufficiently close to each other.
EDIT: A note about conversion to luminance
You don't need to do that at all (as Tony D mentioned already) and if you insist, then do it after the linearization step (as Mark Ransom noted). In a perfect setting (static scene, no noise, no de-mosaicing, no quantization) every channel of every pixel would have the same ratio p1/p2 (if neither is saturated). Therefore the relative weighting of the different channels is irrelevant. You may sum over all pixels/channels (weighing R, G and B equally) or maybe only use the green channel.

volume rendering of large data by packing texture

I want to implement GPU-based ray casting volume rendering of large data sets which are larger than GPU memory. I try to do like this:
(1)Firstly I divide the volume into bricks with equal size.
(2)Then I decide if each brick is transparent or not according the transfer function.
(3)Then I store the non-transparent brick into a "Packed-Texture".In order to sample during ray casting rendering,I create another texture called "Index-Texture" which store the brick index in Packed-Texture.
When I do sampling during rendering in the shader, Firstly I calculate in which brick the sample point is.And then I access the Index-Texture to get the value of the sample point.But in default the value is after interpolated, and is not correctly the index of brick in the Packed-Texture.
So,my question is:when do sampling during ray casting rendering,how to get the index of brick (in which the sample point is) in Packed-Texture from the Index-Texture correctly?
This is not really a problem, say your volume cube goes from [0, 1]³ and you split it into, say 8 blocks into each direction. Then multiply the fractional coordinate with 8 (or whatever) and round down to the nearest integer, which gives you the index. After rounding down you subtract the index from the scaled fractional coordinate, which gives you the fractional position in the sub-block, i.e.
volcube_pos = volcube_frac_pos * volcube_blocks;
volcube_index = (int)floor(volcube_pos);
subblock_pos = volcube_pos - volcube_index;
Now all these blocks have to be stored somewhere. Putting them into a regular, packed 3D texture requires, that you disable filtering, take care about the fencepost problem ( https://stackoverflow.com/a/5879551/524368 ) and do all filtering interpolation yourself.
Another approach instead of packed textures, I'm going to look into myself – as it happens I'm working on volume rendering myself right now – is using NVidia's bindless textures.

Cement Effect - Artistic Effect

I wish to give an effect to images, where the resultant image would appear as if it is painted on a rough cemented background, and the cemented background customizes itself near the edges to highlight them... Please help me in writing an algorithm to generate such an effect.
The first image is the original image
and the second image is the output im looking for.
please note the edges are detected and the mask changes near the edges to indicate the edges clearly
You need to read up on Bump Mapping. There are plenty of bump mapping algorithms.
The basic algorithm is:
for each pixel
Look up the position on the bump map texture that corresponds to the position on the bumped image.
Calculate the surface normal of the bump map
Add the surface normal from step 2 to the geometric surface normal (in case of an image it's a vector pointing up) so that the normal points in a new direction.
Calculate the interaction of the new 'bumpy' surface with lights in the scene using, for example, Phong shading -- light placement is up to you, and decides where will the shadows lie.
Finally, here's a plain C implementation for 2D images.
Starting with
1) the input image as R, G, B, and
2) a texture image, grayscale.
The images are likely in bytes, 0 to 255. Divide it by 255.0 so we have them as being from 0.0 to 1.0. This makes the math easier. For performance, you wouldn't actually do this but instead use clever fixed-point math, an implementation matter I leave to you.
First, to get the edge effects between different colored areas, add or subtract some fraction of the R, G, and B channels to the texture image:
texture_mod = texture - 0.2*R - 0.3*B
You could get fancier with with nonlinear forumulas, e.g. thresholding the R, G and B channels, or computing some mathematical expression involving them. This is always fun to experiment with; I'm not sure what would work best to recreate your example.
Next, compute an embossed version of texture_mod to create the lighting effect. This is the difference of the texture slid up and right one pixel (or however much you like), and the same texture slid. This give the 3D lighting effect.
emboss = shift(texture_mod, 1,1) - shift(texture_mod, -1, -1)
(Should you use texture_mod or the original texture data in this formula? Experiment and see.)
Here's the power step. Convert the input image to HSV space. (LAB or other colorspaces may work better, or not - experiment and see.) Note that in your desired final image, the cracks between the "mesas" are darker, so we will use the original texture_mod and the emboss difference to alter the V channel, with coefficients to control the strength of the effect:
Vmod = V * ( 1.0 + C_depth * texture_mod + C_light * emboss)
Both C_depth and C_light should be between 0 and 1, probably smaller fractions like 0.2 to 0.5 or so. You will need a fudge factor to keep Vmod from overflowing or clamping at its maximum - divide by (1+C_depth+C_light). Some clamping at the bright end may help the highlights look brighter. As always experiment and see...
As fine point, you could also modify the Saturation channel in some way, perhaps decreasing it where texture_mod is lower.
Finally, convert (H, S, Vmod) back to RGB color space.
If memory is tight or performance critical, you could skip the HSV conversion, and apply the Vmod formula instead to the individual R,G, B channels, but this will cause shifts in hue and saturation. It's a tradeoff between speed and good looks.
This is called bump mapping. It is used to give a non flat appearance to a surface.