ttOM123 Posted August 31, 2015 Author Share Posted August 31, 2015 (edited) I didn't use diagrams; I just solved the equations. First, I knew that it would be a homogeneous matrix, since that's a well-known fact, so I just had to find the matrix. I assumed a solution of the form: / Mx * x1 - x0 My * x3 - x0 x0 \ | | | Mx * y1 - y0 My * y3 - y0 y0 | | | \ Mx - 1 My - 1 1 / That may not be an obvious choice at first glance, but I chose it because you can quickly verify that it gives the correct results for (0,0)->(x0,y0), (1,0)->(x1,y1), and (0,1)->(x3,y3). So I just need to find Mx, My so that (1,1)->(x2, y2). I need: (Mx * x1 + My * x3 - x0) / (Mx + My - 1) = x2 (Mx * y1 + My * y3 - y0) / (Mx + My - 1) = y2 Which is: Mx * (x1 - x2) + My * (x3 - x2) = x0 - x2 Mx * (y1 - y2) + My * (y3 - y2) = y0 - y2 Those are just simultaneous equations that can be solved by Cramer's Rule. EDIT: Note I didn't solve the same problem (sort of) solved in the PDF file. That was a quadrilateral to another quadrilateral. I just solved a unit square to a quadrilateral. The quadrilateral to quadrilateral could be solved by finding the matrix that maps a quadrilateral to a unit square; then the solution is simply to map the first quadrilateral to a unit square, then map the unit square to the second quadrilateral. That second mapping can be found by inverting the matrix for the first method. Maybe I'll add a comment later explaining how that can be done fairly easily. Thanks for your explanation, I haven't got time to go though it in depth at the moment but I appreciate it. Edited August 31, 2015 by ttOM123 Link to comment Share on other sites More sharing options...
ttOM123 Posted August 31, 2015 Author Share Posted August 31, 2015 (edited) Your algorithm is backwards. When looping though the y (rows) and x (columns) you should be calculating the destination pixel for that x,y address. You might want to review: http://boltbait.com/pdn/CodeLab/help/overview.asp Yeah I found that out, code should be this which produces the image I posted above, the transformation maps from the selected quadrilateral to the region bounded by the canvas. So this function is nearly there, just need to change to the correct aspect ratio. To run it, copy the picture in first post into paint.net, and copy into Codelab script. Another question, is it possible to enlarge or shrink the canvas size from this script? Or even better, produce the output onto a new image where the script defines the canvas size? Still cannot get the "if" statement to work so to only grab pixels inside the canvas. //if ((XX > rect.Left) && (XX < rect.Right)) { //if ((YY > rect.Top) && (YY < rect.Bottom)) { //dst[x,y] = src[XX,YY]; //} //} To me, it looks like the boundaries are correct, took me a while to work out the origin in the top left of the canvas, not bottom left, and that the y axis is positive downwards. You don't need to check with this particular transformation as I'm always grabbing from inside the canvas, but it would be nice to have. #region UICode int Amount1=0; //[0,100]Slider 1 Description int Amount2=0; //[0,100]Slider 2 Description int Amount3=0; //[0,100]Slider 3 Description #endregion void Render(Surface dst, Surface src, Rectangle rect) { // Delete any of these lines you don't need Rectangle selection = EnvironmentParameters.GetSelection(src.Bounds).GetBoundsInt(); int CenterX = ((selection.Right - selection.Left) / 2)+selection.Left; int CenterY = ((selection.Bottom - selection.Top) / 2)+selection.Top; ColorBgra PrimaryColor = (ColorBgra)EnvironmentParameters.PrimaryColor; ColorBgra SecondaryColor = (ColorBgra)EnvironmentParameters.SecondaryColor; int BrushWidth = (int)EnvironmentParameters.BrushWidth; ColorBgra CurrentPixel; double a = 0.300191; double b = -0.190286; double c = 509.000000; double d = 0.013433; double e = 0.408391; double f = 172.000000; double g = 0.000026; double h = -0.000291; double X, Y; int XX, YY; for (int y = rect.Top; y < rect.Bottom; y++) { if (IsCancelRequested) return; for (int x = rect.Left; x < rect.Right; x++) { X = (a*x + b*y + c) / (g*x + h*y + 1); Y = (d*x + e*y + f) / (g*x + h*y + 1); XX = Convert.ToInt32(X); YY = Convert.ToInt32(Y); dst[x,y] = src[XX,YY]; //if ((XX > rect.Left) && (XX < rect.Right)) { //if ((YY > rect.Top) && (YY < rect.Bottom)) { //dst[x,y] = src[XX,YY]; //} //} } } } Edited August 31, 2015 by ttOM123 Link to comment Share on other sites More sharing options...
MJW Posted August 31, 2015 Share Posted August 31, 2015 (edited) There's no need for the if statement; in fact, you certainly don't want it. You can read pixels from anywhere within the src buffer. If you couldn't, you wouldn't be able to write the plugin. Not allowing reads outside the rect boundaries would mean most of the reads you need to do wouldn't be done. The restriction is on writing to the dst buffer: in the Render routine, you can never write to the dst buffer outside the rect limits (and you can never write to the src buffer). src and dst are the canvas; rect is not the canvas. It's a rather arbitrary region, callled an ROI, within the canvas that PDN has handed to a particular invocation of Render for processing. Read BoltBait's excellent tutorial, which he linked to earlier. A change you should probably make is to replace "dst[x,y] = src[XX,YY]" with "dst[x,y] = "src.GetBilinearSample((float)X, (float)Y)." Instead of taking a point sample from the nearest pixel, GetBilinearSample linearly interpolates between the four surrounding pixels. For situations like in this plugin, that almost always produces a better result. EDIT: Notice it's "src.GetBilinearSample((float)X, (float)Y)" with the doubles X and Y, not "src.GetBilinearSample((float)XX, (float)YY)" with the ints XX and YY. There's hardly ever a good reason to call GetBilinearSample with arguments that have been truncated or rounded to integer values. Also, to answer your question: No, a plugin can't change the canvas size. Edited August 31, 2015 by MJW Link to comment Share on other sites More sharing options...
Eli Posted September 6, 2015 Share Posted September 6, 2015 After all this math conversation, I have a question: Do we have a dll? Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted September 6, 2015 Share Posted September 6, 2015 That's the trouble with theoretical plugin authors - you get theoretical plugins! ebook: Mastering Paint.NET | resources: Plugin Index | Stereogram Tut | proud supporter of Codelab plugins: EER's Plugin Pack | Planetoid | StickMan | WhichSymbol+ | Dr Scott's Markup Renderer | CSV Filetype | dwarf horde plugins: Plugin Browser | ShapeMaker Link to comment Share on other sites More sharing options...
ttOM123 Posted September 6, 2015 Author Share Posted September 6, 2015 Haha I can provide a dll which only does one photo . I've been flat out. Haven't touched it for a week apart from MJW's advice which works nicely. The next hurdle is some method of entering the 4 coordinates of the quadrangle, which I haven't looked into yet. I was thinking of somehow clicking on the canvas 4 times so one can zoom in and be precise then run the plugin, so no menus hopefully. Link to comment Share on other sites More sharing options...
cjbarth Posted July 12, 2019 Share Posted July 12, 2019 You might try Quadrilateral Correction. Link to comment Share on other sites More sharing options...
Recommended Posts