nycos62 Posted January 28, 2019 Author Share Posted January 28, 2019 (edited) new plugin : SpriteSheet Unpacker Hello, is there a recommended approach to identify points clusters or shape center ? (K-means clustering .. ? https://towardsdatascience.com/the-5-clustering-algorithms-data-scientists-need-to-know-a36d136ef68 ) I want to identify each sprite of a packed spritesheet to tile them on a grid step 0 : put transparent color on background, expand canvas size OK step 1 : edge detection => get list of points (inner shape points / outer shape points) OK step 2 : identify clusters / get center point clusters ? with point list ? How to step 3: copy each cluster in center of same size grid cells OK Edited January 28, 2019 by nycos62 Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted January 28, 2019 Share Posted January 28, 2019 ^^ this assumes your sprites are single 'blobs'. What if a sprite is two or three 'objects' with transparent areas in between? The only way I can think of to define a cluster is a flood fill algorithm. Quote 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...
nycos62 Posted January 28, 2019 Author Share Posted January 28, 2019 (edited) 1 hour ago, Ego Eram Reputo said: ^^ this assumes your sprites are single 'blobs'. What if a sprite is two or three 'objects' with transparent areas in between? The only way I can think of to define a cluster is a flood fill algorithm. Unfortunatly, I can't use floodFill because Sprites are touching themselves in some spritesheets, there is not always a pixel between 2 sprites I'm trying technoRobbo basic edge detection with a mix of other edge detection to extract pixel blobs (in blue rectangles, the original sprite sheet has no transparent space between the sprites) For sprite with 2 or 3 objects, I'm thinking of an optional parameter 'Sprite MinX MinY Size' Edited January 28, 2019 by nycos62 Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted January 28, 2019 Share Posted January 28, 2019 If they are touching you"re going to struggle to separate them with code. You really need the [x,y] offset into the sheet and Width x Height of each sprite (if they differ). Save yourself the aggro and manually paste them into a grid? Quote 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...
nycos62 Posted January 28, 2019 Author Share Posted January 28, 2019 6 minutes ago, Ego Eram Reputo said: If they are touching you"re going to struggle to separate them with code I love challenges Quote Link to comment Share on other sites More sharing options...
AndrewDavid Posted January 29, 2019 Share Posted January 29, 2019 @nycos62 Your Grid modifier still crashes when moving the 4th slider. If you want a challenge try and find the math error. File: C:\Program Files\paint.net\Effects\SpriteSheetGridModifier.dll Name: SpriteSheetGridModifierEffect.SpriteSheetGridModifierEffectPlugin Version: 0.1.6963.15631 Author: Copyright ©2019 by Nycos62 Copyright: Modify Cell Grid Size in all layers Website: https://www.getpaint.net/redirect/plugins.html Full error message: PaintDotNet.WorkerThreadException: Worker thread threw an exception ---> System.ArgumentOutOfRangeException: Coordinates out of range, max={Width=799, Height=599} Parameter name: (x,y) Actual value was {X=2,Y=-1}. at PaintDotNet.ExceptionUtil.ThrowArgumentOutOfRangeException(String paramName, Object actualValue, String message) in D:\src\pdn\src\Base\ExceptionUtil.cs:line 88 at PaintDotNet.Surface.GetSetItemThrow(Int32 x, Int32 y) in D:\src\pdn\src\Core\Surface.cs:line 813 at SpriteSheetGridModifierEffect.SpriteSheetGridModifierEffectPlugin.Render(Surface dst, Surface src, Rectangle rect) at SpriteSheetGridModifierEffect.SpriteSheetGridModifierEffectPlugin.OnRender(Rectangle[] rois, Int32 startIndex, Int32 length) at PaintDotNet.Effects.Effect`1.Render(EffectConfigToken parameters, RenderArgs dstArgs, RenderArgs srcArgs, Rectangle[] rois, Int32 startIndex, Int32 length) in D:\src\pdn\src\Effects\Effect`1.cs:line 99 at PaintDotNet.Effects.BackgroundEffectRenderer.RenderWithClipMask(Effect effect, EffectConfigToken token, RenderArgs dstArgs, RenderArgs srcArgs, RectInt32[] rois, IRenderer`1 clipMaskRenderer) in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 196 at PaintDotNet.Effects.BackgroundEffectRenderer.RendererContext.RenderTile(EffectConfigToken token, Int32 tileIndex) in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 175 at PaintDotNet.Effects.BackgroundEffectRenderer.RendererContext.RenderNextTile(EffectConfigToken token) in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 167 at PaintDotNet.Effects.BackgroundEffectRenderer.ThreadFunction() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 267 --- End of inner exception stack trace --- at PaintDotNet.Effects.BackgroundEffectRenderer.DrainExceptions() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 443 at PaintDotNet.Effects.BackgroundEffectRenderer.Abort() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 399 at PaintDotNet.Effects.BackgroundEffectRenderer.Start() in D:\src\pdn\src\PaintDotNet\Effects\BackgroundEffectRenderer.cs:line 345 at PaintDotNet.Menus.EffectMenuBase.<>c__DisplayClass41_4.<RunEffectImpl>b__4() in D:\src\pdn\src\PaintDotNet\Menus\EffectMenuBase.cs:line 926 I think when a negative number is created it causes the crash. But I'm no coder (yet) 1 Quote Link to comment Share on other sites More sharing options...
toe_head2001 Posted January 29, 2019 Share Posted January 29, 2019 45 minutes ago, AndrewDavid said: Your Grid modifier still crashes when moving the 4th slider. I believe this is where the negative values get introduced: On 1/26/2019 at 6:44 PM, nycos62 said: int XDiff = Amount5 - Amount3; int YDiff = Amount6 - Amount4; Are you trying to make the new cells smaller than the original cells? I think this plugin is only meant to increase the cell size. 1 Quote (September 25th, 2023) Sorry about any broken images in my posts. I am aware of the issue. My Gallery | My Plugin Pack Layman's Guide to CodeLab Link to comment Share on other sites More sharing options...
nycos62 Posted January 29, 2019 Author Share Posted January 29, 2019 (edited) 15 hours ago, AndrewDavid said: If you want a challenge try and find the math error. challenge accepted thank you for the tests I never thought to decrease cellSize , toe_head2001 was right // Name: SpriteSheet Grid Modifier // Submenu: PixelArt // Author: Nycos62 // Title: SpriteSheet Grid Modifier // Version: 0.1 // Desc: Modify Cell Grid Size in all layers // Keywords: // URL: // Help: #region UICode IntSliderControl Amount1 = 6; // [0,2000] X Cell number IntSliderControl Amount2 = 6; // [0,2000] Y Cell number IntSliderControl Amount3 = 14; // [0,2000] Original Cell width (X) IntSliderControl Amount4 = 14; // [0,2000] Original Cell height (Y) IntSliderControl Amount5 = 20; // [0,2000] Desired Cell width (New X) IntSliderControl Amount6 = 20; // [0,2000] Desired Cell height (New Y) #endregion void Render(Surface dst, Surface src, Rectangle rect) { //clear dest dst.Clear(rect, ColorBgra.Transparent); int XOffset = 0; int YOffset = 0; int XDiff = Amount5 - Amount3; int YDiff = Amount6 - Amount4; if (XDiff %2 == 0) XOffset = XDiff / 2; else XOffset = (int)(XDiff / 2); //TODO verify if (YDiff %2 == 0) YOffset = YDiff / 2; else YOffset = (int)(YDiff / 2); //TODO verify int XOffsetAfter = XDiff - XOffset; int YOffsetAfter = YDiff - YOffset; for (int cx = 0; cx < Amount1; cx++) { int XDecay = Math.Max(cx,0) * XOffsetAfter; for (int cy = 0; cy < Amount2; cy++) { int YDecay = Math.Max(cy,0) * YOffsetAfter; if (IsCancelRequested) return; //Copy cell with Offset for (int cell_posX = 0; cell_posX < Amount3; cell_posX++) { for (int cell_posY = 0; cell_posY < Amount4; cell_posY++) { int Xdest = cx * Amount3 + cell_posX + (cx+1) * XOffset + XDecay; int Ydest = cy * Amount4 + cell_posY + (cy+1) * YOffset + YDecay; int XSource = cx * Amount3 + cell_posX; int YSource = cy * Amount4 + cell_posY; //avoid drawing out of bounds dst/src [x,y] if (Xdest < dst.Bounds.Width && Ydest < dst.Bounds.Height && XSource < dst.Bounds.Width && YSource < dst.Bounds.Height && Xdest > 0 && Ydest > 0 && Xdest < Amount1 * Amount5 && Ydest < Amount2 * Amount6) dst[Xdest, Ydest] = src[XSource, YSource]; } } } } } Edited January 29, 2019 by nycos62 1 Quote Link to comment Share on other sites More sharing options...
AndrewDavid Posted January 29, 2019 Share Posted January 29, 2019 Well Done @nycos62 Issue resolved. 1 Quote Link to comment Share on other sites More sharing options...
nycos62 Posted January 30, 2019 Author Share Posted January 30, 2019 (edited) new plugin : Pixel Art Palette Reverse (dll=> ReversePalette.zip) (spritesheet unpacker plugin is paused...) *remember it's for pixel Art so if you use huge picture with huge amount of different color it going to be slow as hell purpose : extract and manipulate palette you want to apply shadow modification on sprite, you want to invert palette of sprite First check box shows palette grabbed in the selection, if no selection, full picture is processed Future : Add Reverse "By Color Family", TODO.. (need to code how to sort palette by color HUE range and reverse it) with "User Choice", you draw on the third line of pixels your custom palette to change colors directly on the result (if your canvas width is smaller than palette and you are using "User Choice" option, colors out of canvas are replaced with transparent // Name: Pixel Art Reverse Palette // Submenu: PixelArt // Author: Nycos62 // Title: Reverse palette in selected area(s) // Version: 0.1 // Desc: reversing the palette in selected zone // Keywords: // URL: // Help: #region UICode bool Amount1 = true; // [0,1] Show Palette and Reversed Palette bool Amount2 = false; // [0,1] Apply modified Palette byte Amount3 = 0; // Reversion Method|Full palette Sort|Full Intensity|By Grabbing Order|By Color Family(TODO...)|User choice(Draw Your custom Palette on 3rd line) bool Amount4 = true; //[0,1] Ignore transparent Color (0,0,0,0) bool Amount5 = true; //[0,1] Ignore semi transparent Colors #endregion void Render(Surface dst, Surface src, Rectangle rect) { //Getting selected Pixels System.Drawing.Drawing2D.RegionData selection = EnvironmentParameters.GetSelection(src.Bounds).GetRegionData(); Region reg = new Region(selection); RectangleF[] rects = reg.GetRegionScans(new Matrix()); //Grabbing Palette List<ColorBgra> colorsInSelection = new List<ColorBgra>(); foreach(RectangleF r in rects) { for (int y = (int)r.Top; y < r.Bottom; y++) { if (IsCancelRequested) return; if (y > 2)//Ignore top 3 lines to grab palette for (int x = (int)r.Left; x < r.Right; x++) { if (!colorsInSelection.Contains(src[x,y])) { if ((Amount5 && src[x,y].A == 255) || !Amount5) if ((Amount4 && src[x,y] != ColorBgra.Transparent) || !Amount4) colorsInSelection.Add(src[x,y]); } } } } //Copy into Result Palette System.Collections.ArrayList sortedColors = new System.Collections.ArrayList(); for (int i = 0; i < colorsInSelection.Count; i++) { ColorBgra col = new ColorBgra(); col.A = colorsInSelection[i].A; col.B = colorsInSelection[i].B; col.G = colorsInSelection[i].G; col.Bgra = colorsInSelection[i].Bgra; sortedColors.Add(col); } //ApplySort if (Amount3 == 0 || Amount3 == 4) sortedColors.Sort(new _ColorSorter()); if (Amount3 == 1) sortedColors.Sort(new _IntensitySorter()); //Copy Sorted Palette To Original palette colorsInSelection = new List<ColorBgra>(); for (int i = 0; i < sortedColors.Count; i++) { ColorBgra col = new ColorBgra(); col.A = ((ColorBgra)sortedColors[i]).A; col.B = ((ColorBgra)sortedColors[i]).B; col.G = ((ColorBgra)sortedColors[i]).G; col.Bgra = ((ColorBgra)sortedColors[i]).Bgra; colorsInSelection.Add(col); } if (IsCancelRequested) return; //Reverse Palette if (Amount3 == 4) { sortedColors = new System.Collections.ArrayList(); for (int x = 0; x < colorsInSelection.Count; x++) { if (x < dst.Bounds.Width) { sortedColors.Add(dst[x,2]); } else { sortedColors.Add(ColorBgra.Transparent); } } } else sortedColors.Reverse(); //Show palette on upperLeft image corner for (int i = 0; i < dst.Bounds.Width; i++) { if (Amount1 && i<colorsInSelection.Count) { dst[i,0] = colorsInSelection[i]; dst[i,1] = (ColorBgra)sortedColors[i]; } else { dst[i,0] = src[i,0]; dst[i,1] = src[i,1]; } } if (IsCancelRequested) return; //Apply change foreach(RectangleF r in rects) { for (int y = (int)r.Top; y < r.Bottom; y++) { if (IsCancelRequested) return; for (int x = (int)r.Left; x < r.Right; x++) { //avoid draw on palette if (!(x < colorsInSelection.Count && y < 3)) { if (Amount2) { if (colorsInSelection.Contains(src[x,y])) dst[x,y] = (ColorBgra)sortedColors[colorsInSelection.IndexOf(src[x,y])]; } else { dst[x,y] = src[x,y]; } } } } } } internal class _ColorSorter: System.Collections.IComparer { public int Compare (object x, object y) { // local variables Color cx, cy; float hx, hy, sx, sy, bx, by; // get Color values cx = ((ColorBgra) x).ToColor(); cy = ((ColorBgra) y).ToColor(); // get saturation values sx = cx.GetSaturation (); sy = cy.GetSaturation (); // get hue values hx = cx.GetHue (); hy = cy.GetHue (); // get brightness values bx = cx.GetBrightness (); by = cy.GetBrightness (); // determine order // 1 : hue if (hx < hy) return -1; else if (hx > hy) return 1; else { // 2 : saturation if (sx < sy) return -1; else if (sx > sy) return 1; else { // 3 : brightness if (bx < by) return -1; else if (bx > by) return 1; else return 0; } } } } internal class _IntensitySorter: System.Collections.IComparer { public int Compare (object x, object y) { return ((ColorBgra)x).GetIntensity().CompareTo(((ColorBgra)y).GetIntensity()); } } cheers ! Sorting Colors "By Family" : public string Classify(Color c) { float hue = c.GetHue(); float sat = c.GetSaturation(); float lgt = c.GetLightness(); if (lgt < 0.2) return "Blacks"; if (lgt > 0.8) return "Whites"; if (sat < 0.25) return "Grays"; if (hue < 30) return "Reds"; if (hue < 90) return "Yellows"; if (hue < 150) return "Greens"; if (hue < 210) return "Cyans"; if (hue < 270) return "Blues"; if (hue < 330) return "Magentas"; return "Reds"; } Edited January 30, 2019 by nycos62 1 Quote Link to comment Share on other sites More sharing options...
nycos62 Posted January 31, 2019 Author Share Posted January 31, 2019 wow... colors are really complicated to classify : what is the limit between a brown, a grey, a black, and an orange, a yellow, a green color ?.. need to add trackbars to adjust palette color range detection depending on the picture palette... Quote Link to comment Share on other sites More sharing options...
Ego Eram Reputo Posted January 31, 2019 Share Posted January 31, 2019 Color Family is an odd way to select shades - as you've found Why not use the Colorwheel control to give the user all the color options they would ever need? Quote 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...
nycos62 Posted October 5, 2020 Author Share Posted October 5, 2020 (edited) Hi folks, this is the old codelab plugin palette switcher, but first read this : Pixel Art Palette Switcher.zip I was trying to achieve this kind of effect from a photo I did not find a plugin to get the palette of image and change the colors so what have I done, I remembered my old palette switcher attempt with code lab first get the palette of the lion with TR's Color Reducer, because of internet picture (jpg sadness... ... ) and save the palette, as lion.txt then I took a random picture to test, my daughter for exemple : here are the steps : Gaussian Blur Radius 10 Black and White Posterize (23) ( because Lion.txt palette have 23 colors ) then I used my old plugin Palette Switcher run the plugin palette switcher once, it will draw on first line of the picture the current palette, and click OK then draw the replacement palette on the 3rd line then run the palette switcher plugin once again Choose Reversion Method 'User Choice' and check the checkbox apply palette another quick sample : Spoiler Spoiler Spoiler Spoiler Spoiler Spoiler DO NOT USE ON A NON REDUCED PALETTE IMAGE WITH POSTERIZATION OR ELSE, OR IT WILL TAKE FOREVER TO COMPUTE THE RESULT If someone knows a better way to achieve this, please let me know Cheers ! Edited October 5, 2020 by nycos62 1 1 Quote Link to comment Share on other sites More sharing options...
Reptillian Posted October 5, 2020 Share Posted October 5, 2020 (edited) These effects are generally achieved by manually creating the areas (likely in vector format since you can manipulate lines easier with it). Theoretically, you can achieve better results than what you did using anisotropic smoothing based on partial differential equations, and inpainting. I don't know how to describe that in terms of c# or more clearer, but it's something to look into if you want better result. Regardless I like the result, but making it better would take a lot of time, and a lot of research. Edited October 5, 2020 by Reptillian Quote G'MIC Filter Developer Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.