Jump to content

NLCbanner2024.jpg.2478be509670e60c2d6efd04834b8b47.jpg

Yet another stacker taking form


GreatAttractor

Recommended Posts

I've been putting together a stacking program and today I got all the building blocks working (after a fashion) for the first time. Still lots to do of course (tuning, handling of boundary conditions and testing, testing...), but here's the "first light" (with a touch of unsharp mask):

stack_test03.jpg.7009c89faa3bc1a7a17c499

Link to comment
Share on other sites

38 minutes ago, skybadger said:

I want a fully text-based acquisition, centring and stacking tool for headless Linux. #tell me you can do this !!.

Of course. All processing is contained in a stand-alone library written in C99. Preparing a console-oriented, scriptable wrapper will be easy. Besides, everything will be GPLed.

 

41 minutes ago, skybadger said:

What about those features top right for frame boundaries

Did I mention testing & boundary conditions? ;) Right now the quality estimation behaves strangely at times and everything is crashing a lot, but I must say it's at least fast (and I haven't enabled multithreading yet).

Link to comment
Share on other sites

56 minutes ago, pipnina said:

What languages/IDEs you using for this? As someone who is looking into software development as a career this is very interesting!

C (C99) and C++ (C++11). My current IDE is Code::Blocks (on Linux), but I use Eclipse and MS Visual Studio occasionally.

59 minutes ago, pipnina said:

Also, what is the end goal with this? Curious!

A portable (I use mostly Linux), fast stacker with a clean GUI. Just like other astrophotographers, I use and admire the existing stackers, but they don't have all the features I'd like.

Link to comment
Share on other sites

12 minutes ago, GreatAttractor said:

C (C99) and C++ (C++11). My current IDE is Code::Blocks (on Linux), but I use Eclipse and MS Visual Studio occasionally.

A portable (I use mostly Linux), fast stacker with a clean GUI. Just like other astrophotographers, I use and admire the existing stackers, but they don't have all the features I'd like.

I was learning C++ and use Code::Blocks to learn it. I was planning on trying to make my own stacker myself but it would take me about 10 years due to me being super inexperienced haha. Made a few design documents outlinign what I wanted it to do and a little on how ii would look but it's been unchanged since about 6 months ago lol.

Good luck with your project! Hope it's going a lot better than mine!

Link to comment
Share on other sites

I started looking at this using opencv - it seems others have managed to do it with this. The downsire I have is that mosg apps nowadays seem to start with a gui and code into that rather than creating a powerful library and than coding a gui against that.

How do I get to see you stuff ?

Will it run on a raspbpi ?

Many thanks

Mike

 

 

Link to comment
Share on other sites

41 minutes ago, skybadger said:

The downsire I have is that mosg apps nowadays seem to start with a gui and code into that rather than creating a powerful library and than coding a gui against that.

It felt natural to me to put it in a library. The lib's interface is simple, just initiate and go step-by-step through those few subsequent phases (and optionally get some "diagnostic visual output" for every step if desired). I already have the GUI thought-through, coding it will be straighforward.

49 minutes ago, skybadger said:

How do I get to see you stuff ?

Once it's usable, I'll post a beta version (...or alpha) eventually.

50 minutes ago, skybadger said:

Will it run on a raspbpi ?

Sure. I'll use wxWidgets for GUI, as usual, so it will be possible to build on most platforms.

Link to comment
Share on other sites

I've made a crucial fix to reference points alignment and things look much better now (and still run fast!). With this out of the way I don't see any other serious problems, now it's just improvements, tuning and handling of corner cases.

Another sample stack (only the middle region was stacked, so that for now I can skip the careful handling of image borders):

stack_unsh.png.2062788ee05e822a258d28e0a

Link to comment
Share on other sites

  • 2 weeks later...

I've decided to (learn and) use GTK (gtkmm) for GUI; the API is nice and (after a few days of adjusting) easy to use.

Below is a recorded test run (stacking of 500 frames). Note that this is not the final performance – this run was single-threaded and the enabled "processing preview" slows things down (not to mention the screen grabber encoding in the background).

 

 

Link to comment
Share on other sites

  • 1 month later...

Qt4 is reasonable if you want a cross portable option for GUI.

It would be very easy to make a multi-image dithering system by taking the transform for each image and sampling to make a super-resolution. Given the number of images that's likely to return a nice detail enhancement.

 

Link to comment
Share on other sites

21 hours ago, michael.h.f.wilkinson said:

I trust this project will support bigger images

64 bits will be the only limit  :)

 

5 hours ago, NickK said:

Qt4 is reasonable if you want a cross portable option for GUI.

I decided against Qt due to those required intermediate steps (qtmake etc.). GTK/gtkmm uses pure C++11 for "signals" handling and I could embed widgets' creation and initialization directly in the source code (the GUI is simple here, after all).

 

5 hours ago, NickK said:

It would be very easy to make a multi-image dithering system

Sure, I'm open to any suggestions and contributions.

I'm glad to announce that the first release is coming soon, all the last weekend's material was stacked with this tool.

Link to comment
Share on other sites

35 minutes ago, GreatAttractor said:

Sure, I'm open to any suggestions and contributions.

I'm glad to announce that the first release is coming soon, all the last weekend's material was stacked with this tool.

Doh - just noted the source was on github.

I have a star gazing ubuntu VM (intel) that I use for the up coming odroid project. I'll have a look at compiling this and look at pointers for drizzle/super resolution.

I had a quick look at the code base and it seems that it should be possible.

Link to comment
Share on other sites

Looking at the images - it looks like you have a vector space that shows movement over time. Should be straight forward to make a mapping system (even over time if you have enough oomph) for the interpolation.

I did it in OpenCL but it should be straight forward to implement the same routines in normal SSE/CPU.

 

Link to comment
Share on other sites

Very scrappy code on this - takes a image, the offset in alignment (transition) and performs a Lanczos interpolation and outputs an upscaled image.

For each pixel this is executed, where the GPU performs interpolation and upscale.

	__kernel void translateSubPixelLanczosThreeScalemono(__global float* imgInput,
	                                                     __global float* imgOutputOut,
	                                                     __global float* imgDelta,
	                                                     float regionOriginX, float regionOriginY,
	                                                     float regionWidth, float regionHeight,
	                                                     uint fftWidth, uint fftHeight,
	                                                     uint texWidth, uint texHeight,
	                                                     float sX, float sY,
	                                                     float stride) {
	    
	    float coordOutputInX = sX + (((float)get_global_id())) ;
	    float coordOutputInY = sY + (((float)get_global_id(1))) ;
	    
	    // float fracIX = coordOutputInX/stride - floor(coordOutputInX/stride);
	    // float fracIY = coordOutputInY/stride - floor(coordOutputInY/stride);
	    
	    int outputOutPos = (int) ( coordOutputInX + coordOutputInY*((float)fftWidth) );
	    
	    float value = (float)(0.0f); // ((float)0x7fff); // (float)(0.0f); // use 0x7fff to show external rectangle
	    
	    // at the stride so need to perform input image processing - otherwise copy the ping-pong
	    // if( (fracIX<0.0001f) && (fracIY<0.0001f) ) {
	    
	    // find the input value....
	    
	    float coordX =   (((float)get_global_id()));
	    float coordY =   (((float)get_global_id(1)));
	    
	    float deltaX = (float) imgDelta[];
	    float deltaY = (float) imgDelta[1];
	    
	    float shiftX = deltaX;
	    float shiftY = deltaY;
	    
	    //    float shiftX=0.0f;
	    //    float shiftY=0.0f;
	    //
	    //    if( deltaX > (((float)fftWidth)/2.0f)) {
	    //        shiftX = deltaX - ((float)fftWidth) - 1.0f;
	    //    } else {
	    //        shiftX =  deltaX - 1.0f;
	    //    }
	    //
	    //    if( deltaY > (((float)fftHeight)/2.0f) ) {
	    //        shiftY = deltaY - ((float)fftHeight) - 1.0f;
	    //    } else {
	    //        shiftY = deltaY - 1.0f;
	    //    }
	    
	    // input texture is 659x495
	    // input scale offset+range, ie 260+0..60, 215+0..60
	    // interpolation would be the output texture size with the range
	    // output scale is 60/659 and 60/495 to fit the target texture
	    // hence
	    //   for(ix = regionOriginX; ix< regionOriginX+regionWidth; ix+= regionWith/texWidth)
	    //   and ox = 0 to textWidth
	    // ...
	    //   ox = 0 to texwidth
	    //   ix = regionOriginX+shiftX + ox*(regionWith/texWidth)
	    // ...
	    //   interpolation is value between input values, or regionWidth, hence ox mod (texWidth/regionWidth)
	    //   or
	    //   interpolation fraction = ox*(regionWidth/texWidth) - floor(ox*(regionWidth/texWidth))
	    
	    // shift the input image to the reference.
	    // for every (x,y) of the output image, we look at the imput image to find the value.
	    // normally that would be:
	    //    output (x,y) = input(x+shiftX, y+shiftY)
	    
	    float scaleX = regionWidth / ((float)fftWidth);
	    float scaleY = regionHeight / ((float)fftHeight);
	    
	    // sample input
	    // starting point + coord * length / texture samples
	    float sCx = coordX*scaleX;
	    float sCy = coordY*scaleY;
	    float iX = regionOriginX + shiftX +sCx;
	    //float iY = regionOriginY + shiftY +sCy; // was the coord-shift for Y
	    float iY = regionOriginY + sCy - shiftY;
	    
	    uint maxSize = fftWidth*fftHeight;
	    
	    if( (iX>=3.0f) && (iX < ((float)fftWidth-3))
	       && (iY>=3.0f) && (iY < ((float)fftHeight-3)) ) {
	 
	 
	 
	        
	        int intX = (int)floor(iX) ;
	        int intY = (int)floor(iY) ;
	        
	        float fracX = sCx - floor(sCx);;
	        float fracY = sCy - floor(sCy);
	        
	        float order = 3.0f;
	                
	        float pixelValue;
	        float subPixelDistance;
	        float top, bottom;
	        float functionValue;
	        float pixelIntegral =0.0f;
	        
	        // summation
	        
	        // S( intX - 3 )
	        pixelValue = imgInput[fftWidth*intY+(intX-3)];
	        subPixelDistance = iX - (intX-3); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intX - 2 )
	        pixelValue = imgInput[fftWidth*intY+(intX-2)];
	        subPixelDistance = iX - (intX-2); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        
	        // S( intX - 1 )
	        pixelValue = imgInput[fftWidth*intY+(intX-1)];
	        subPixelDistance = iX - (intX-1
	                                 ); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intX - 0 )
	        pixelValue = imgInput[fftWidth*intY+(intX)];
	        subPixelDistance = iX - (intX); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intX + 1 )
	        pixelValue = imgInput[fftWidth*intY+(intX+1)];
	        subPixelDistance = iX - (intX+1); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intX + 2 )
	        pixelValue = imgInput[fftWidth*intY+(intX+2)];
	        subPixelDistance = iX - (intX+2); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intX + 3 )
	        pixelValue = imgInput[fftWidth*intY+(intX+3)];
	        subPixelDistance = iX - (intX+3); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        
	        
	        // S( intY - 3 )
	        pixelValue = imgInput[fftWidth*(intY-3)+intX];
	        subPixelDistance = iY - (intY-3); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intY - 2 )
	        pixelValue = imgInput[fftWidth*(intY-2)+intX];
	        subPixelDistance = iY - (intY-2); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intY - 1 )
	        pixelValue = imgInput[fftWidth*(intY-1)+intX];
	        subPixelDistance = iY - (intY-1); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intY - 0 )
	        pixelValue = imgInput[fftWidth*(intY)+intX];
	        subPixelDistance = iY - (intY); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intY + 1 )
	        pixelValue = imgInput[fftWidth*(intY+1)+intX];
	        subPixelDistance = iY - (intY+1); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intY + 2 )
	        pixelValue = imgInput[fftWidth*(intY+2)+intX];
	        subPixelDistance = iY - (intY+2); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        // S( intY + 3 )
	        pixelValue = imgInput[fftWidth*(intY+3)+intX];
	        subPixelDistance = iY - (intY+3); // positive distance from the top left (assume radial distribution)
	        top = order * sinpi(subPixelDistance)*sinpi(subPixelDistance/order); // a*sin(pi*x)*sin(pi*x/a) / (pi*pi*x*x)
	        bottom = M_PI_F*M_PI_F * subPixelDistance*subPixelDistance;
	        functionValue = top / bottom;
	        if( subPixelDistance == 0.0f ) functionValue = 1.0f; // can be optimised
	        if( subPixelDistance > order ) functionValue = 0.0f;
	        pixelIntegral+= pixelValue*functionValue;
	        
	        value = pixelIntegral/2.0f;
	        
	    }
	    //}
	    
	    imgOutputOut[ outputOutPos ] = value;
	}
	

 

When stacked with interpolated upscaled images, the result is super resolution.

 

Although the code is probably not much in terms of direct use - this is what I've found to be a good way to get it done. In the pipeline I wrote a while back this has a scale/rotate and then transition with phase correlated images using GPU for everything. Hence it's a little different in the approach.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. By using this site, you agree to our Terms of Use.