模仿这种成像效果

Jam*_*uth 5 c# graphics imageprocessor

我问这个问题是因为另一个问题已经两年了并且没有准确回答。

我希望用 C#复制本文中提到的 PhotoShop 效果。Adobe 称它为彩色半色调,我认为它看起来像是某种旋转的 CMYK 半色调。无论哪种方式,我都不知道我会怎么做。

当前代码示例如下。

有任何想法吗?

彩色半色调效果

聚苯乙烯

这不是家庭作业。我希望升级我的 OSS 项目ImageProcessor 中的漫画书效果。

ImageProcessor 漫画效果

进度更新。

所以这里有一些代码来显示我到目前为止所做的......

我可以相当容易和准确地在 CMYK 和 RGB 之间进行转换,足以满足我的需要,并且还可以根据一系列点的每个颜色分量的强度打印出一系列有图案的椭圆。

我刚才遇到的是旋转每种颜色的图形对象,以便将点放置在代码中指定的角度。任何人都可以给我一些关于如何去做的指示吗?

public Image ProcessImage(ImageFactory factory)
{
    Bitmap newImage = null;
    Image image = factory.Image;

    try
    {
        int width = image.Width;
        int height = image.Height;

        // These need to be used.
        float cyanAngle = 105f;
        float magentaAngle = 75f;
        float yellowAngle = 90f;
        float keylineAngle = 15f;

        newImage = new Bitmap(width, height);
        newImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

        using (Graphics graphics = Graphics.FromImage(newImage))
        {
            // Reduce the jagged edges.
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
            graphics.CompositingQuality = CompositingQuality.HighQuality;

            graphics.Clear(Color.White);

            using (FastBitmap sourceBitmap = new FastBitmap(image))
            {
                for (int y = 0; y < height; y += 4)
                {
                    for (int x = 0; x < width; x += 4)
                    {
                        Color color = sourceBitmap.GetPixel(x, y);

                        if (color != Color.White)
                        {
                            CmykColor cmykColor = color;
                            float cyanBrushRadius = (cmykColor.C / 100) * 3;
                            graphics.FillEllipse(Brushes.Cyan, x, y, cyanBrushRadius, cyanBrushRadius);

                            float magentaBrushRadius = (cmykColor.M / 100) * 3;
                            graphics.FillEllipse(Brushes.Magenta, x, y, magentaBrushRadius, magentaBrushRadius);

                            float yellowBrushRadius = (cmykColor.Y / 100) * 3;
                            graphics.FillEllipse(Brushes.Yellow, x, y, yellowBrushRadius, yellowBrushRadius);

                            float blackBrushRadius = (cmykColor.K / 100) * 3;
                            graphics.FillEllipse(Brushes.Black, x, y, blackBrushRadius, blackBrushRadius);
                        }
                    }
                }
            }
        }

        image.Dispose();
        image = newImage;
    }
    catch (Exception ex)
    {
        if (newImage != null)
        {
            newImage.Dispose();
        }

        throw new ImageProcessingException("Error processing image with " + this.GetType().Name, ex);
    }

    return image;
}
Run Code Online (Sandbox Code Playgroud) 输入图像

输入图像

电流输出

正如您所看到的,由于绘制的椭圆没有倾斜,因此颜色输出不正确。

电流输出

Jam*_*uth 0

这是一个可行的解决方案。它并不漂亮,也不快(在我的笔记本电脑上为 2 秒),但输出很好。尽管我认为他们正在执行一些额外的工作,但它与 Photoshop 的输出并不完全匹配。

\n\n

轻微的摩尔纹\xc3\xa9 图案有时会出现在不同的测试图像上,但去网纹超出了当前问题的范围。

\n\n

该代码执行以下步骤。

\n\n
    \n
  1. 以给定的间隔循环遍历图像的像素
  2. \n
  3. 对于每个颜色分量,CMYK 在给定点绘制一个椭圆,该椭圆是通过将当前点旋转设定角度来计算的。该椭圆的尺寸由每个点处每个颜色分量的级别确定。
  4. \n
  5. 通过循环遍历像素点并在每个点添加 CMYK 颜色分量值来创建新图像,以确定绘制到图像的正确颜色。
  6. \n
\n\n

输出图像

\n\n

半色调图像

\n\n

代码\n

\n\n
public Image ProcessImage(ImageFactory factory)\n{\n    Bitmap cyan = null;\n    Bitmap magenta = null;\n    Bitmap yellow = null;\n    Bitmap keyline = null;\n    Bitmap newImage = null;\n    Image image = factory.Image;\n\n    try\n    {\n        int width = image.Width;\n        int height = image.Height;\n\n        // Angles taken from Wikipedia page.\n        float cyanAngle = 15f;\n        float magentaAngle = 75f;\n        float yellowAngle = 0f;\n        float keylineAngle = 45f;\n\n        int diameter = 4;\n        float multiplier = 4 * (float)Math.Sqrt(2);\n\n        // Cyan color sampled from Wikipedia page.\n        Brush cyanBrush = new SolidBrush(Color.FromArgb(0, 153, 239));\n        Brush magentaBrush = Brushes.Magenta;\n        Brush yellowBrush = Brushes.Yellow;\n        Brush keylineBrush;\n\n        // Create our images.\n        cyan = new Bitmap(width, height);\n        magenta = new Bitmap(width, height);\n        yellow = new Bitmap(width, height);\n        keyline = new Bitmap(width, height);\n        newImage = new Bitmap(width, height);\n\n        // Ensure the correct resolution is set.\n        cyan.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        magenta.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        yellow.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        keyline.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        newImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n\n        // Check bounds against this.\n        Rectangle rectangle = new Rectangle(0, 0, width, height);\n\n        using (Graphics graphicsCyan = Graphics.FromImage(cyan))\n        using (Graphics graphicsMagenta = Graphics.FromImage(magenta))\n        using (Graphics graphicsYellow = Graphics.FromImage(yellow))\n        using (Graphics graphicsKeyline = Graphics.FromImage(keyline))\n        {\n            // Ensure cleared out.\n            graphicsCyan.Clear(Color.Transparent);\n            graphicsMagenta.Clear(Color.Transparent);\n            graphicsYellow.Clear(Color.Transparent);\n            graphicsKeyline.Clear(Color.Transparent);\n\n            // This is too slow. The graphics object can\'t be called within a parallel \n            // loop so we have to do it old school. :(\n            using (FastBitmap sourceBitmap = new FastBitmap(image))\n            {\n                for (int y = -height * 2; y < height * 2; y += diameter)\n                {\n                    for (int x = -width * 2; x < width * 2; x += diameter)\n                    {\n                        Color color;\n                        CmykColor cmykColor;\n                        float brushWidth;\n\n                        // Cyan\n                        Point rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), cyanAngle);\n                        int angledX = rotatedPoint.X;\n                        int angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.C / 255f) * multiplier;\n                            graphicsCyan.FillEllipse(cyanBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n\n                        // Magenta\n                        rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), magentaAngle);\n                        angledX = rotatedPoint.X;\n                        angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.M / 255f) * multiplier;\n                            graphicsMagenta.FillEllipse(magentaBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n\n                        // Yellow\n                        rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), yellowAngle);\n                        angledX = rotatedPoint.X;\n                        angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.Y / 255f) * multiplier;\n                            graphicsYellow.FillEllipse(yellowBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n\n                        // Keyline \n                        rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), keylineAngle);\n                        angledX = rotatedPoint.X;\n                        angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.K / 255f) * multiplier;\n\n                            // Just using blck is too dark. \n                            keylineBrush = new SolidBrush(CmykColor.FromCmykColor(0, 0, 0, cmykColor.K));\n                            graphicsKeyline.FillEllipse(keylineBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n                    }\n                }\n            }\n\n            // Set our white background.\n            using (Graphics graphics = Graphics.FromImage(newImage))\n            {\n                graphics.Clear(Color.White);\n            }\n\n            // Blend the colors now to mimic adaptive blending.\n            using (FastBitmap cyanBitmap = new FastBitmap(cyan))\n            using (FastBitmap magentaBitmap = new FastBitmap(magenta))\n            using (FastBitmap yellowBitmap = new FastBitmap(yellow))\n            using (FastBitmap keylineBitmap = new FastBitmap(keyline))\n            using (FastBitmap destinationBitmap = new FastBitmap(newImage))\n            {\n                Parallel.For(\n                    0,\n                    height,\n                    y =>\n                    {\n                        for (int x = 0; x < width; x++)\n                        {\n                            // ReSharper disable AccessToDisposedClosure\n                            Color cyanPixel = cyanBitmap.GetPixel(x, y);\n                            Color magentaPixel = magentaBitmap.GetPixel(x, y);\n                            Color yellowPixel = yellowBitmap.GetPixel(x, y);\n                            Color keylinePixel = keylineBitmap.GetPixel(x, y);\n\n                            CmykColor blended = cyanPixel.AddAsCmykColor(magentaPixel, yellowPixel, keylinePixel);\n                            destinationBitmap.SetPixel(x, y, blended);\n                            // ReSharper restore AccessToDisposedClosure\n                        }\n                    });\n            }\n        }\n\n        cyan.Dispose();\n        magenta.Dispose();\n        yellow.Dispose();\n        keyline.Dispose();\n        image.Dispose();\n        image = newImage;\n    }\n    catch (Exception ex)\n    {\n        if (cyan != null)\n        {\n            cyan.Dispose();\n        }\n\n        if (magenta != null)\n        {\n            magenta.Dispose();\n        }\n\n        if (yellow != null)\n        {\n            yellow.Dispose();\n        }\n\n        if (keyline != null)\n        {\n            keyline.Dispose();\n        }\n\n        if (newImage != null)\n        {\n            newImage.Dispose();\n        }\n\n        throw new ImageProcessingException("Error processing image with " + this.GetType().Name, ex);\n    }\n\n    return image;\n} \n
Run Code Online (Sandbox Code Playgroud)\n\n

用于旋转像素的附加代码如下。这可以在围绕另一个点旋转一个点中找到

\n\n

为了简洁起见,我省略了颜色添加代码。

\n\n
public Image ProcessImage(ImageFactory factory)\n{\n    Bitmap cyan = null;\n    Bitmap magenta = null;\n    Bitmap yellow = null;\n    Bitmap keyline = null;\n    Bitmap newImage = null;\n    Image image = factory.Image;\n\n    try\n    {\n        int width = image.Width;\n        int height = image.Height;\n\n        // Angles taken from Wikipedia page.\n        float cyanAngle = 15f;\n        float magentaAngle = 75f;\n        float yellowAngle = 0f;\n        float keylineAngle = 45f;\n\n        int diameter = 4;\n        float multiplier = 4 * (float)Math.Sqrt(2);\n\n        // Cyan color sampled from Wikipedia page.\n        Brush cyanBrush = new SolidBrush(Color.FromArgb(0, 153, 239));\n        Brush magentaBrush = Brushes.Magenta;\n        Brush yellowBrush = Brushes.Yellow;\n        Brush keylineBrush;\n\n        // Create our images.\n        cyan = new Bitmap(width, height);\n        magenta = new Bitmap(width, height);\n        yellow = new Bitmap(width, height);\n        keyline = new Bitmap(width, height);\n        newImage = new Bitmap(width, height);\n\n        // Ensure the correct resolution is set.\n        cyan.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        magenta.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        yellow.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        keyline.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n        newImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);\n\n        // Check bounds against this.\n        Rectangle rectangle = new Rectangle(0, 0, width, height);\n\n        using (Graphics graphicsCyan = Graphics.FromImage(cyan))\n        using (Graphics graphicsMagenta = Graphics.FromImage(magenta))\n        using (Graphics graphicsYellow = Graphics.FromImage(yellow))\n        using (Graphics graphicsKeyline = Graphics.FromImage(keyline))\n        {\n            // Ensure cleared out.\n            graphicsCyan.Clear(Color.Transparent);\n            graphicsMagenta.Clear(Color.Transparent);\n            graphicsYellow.Clear(Color.Transparent);\n            graphicsKeyline.Clear(Color.Transparent);\n\n            // This is too slow. The graphics object can\'t be called within a parallel \n            // loop so we have to do it old school. :(\n            using (FastBitmap sourceBitmap = new FastBitmap(image))\n            {\n                for (int y = -height * 2; y < height * 2; y += diameter)\n                {\n                    for (int x = -width * 2; x < width * 2; x += diameter)\n                    {\n                        Color color;\n                        CmykColor cmykColor;\n                        float brushWidth;\n\n                        // Cyan\n                        Point rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), cyanAngle);\n                        int angledX = rotatedPoint.X;\n                        int angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.C / 255f) * multiplier;\n                            graphicsCyan.FillEllipse(cyanBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n\n                        // Magenta\n                        rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), magentaAngle);\n                        angledX = rotatedPoint.X;\n                        angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.M / 255f) * multiplier;\n                            graphicsMagenta.FillEllipse(magentaBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n\n                        // Yellow\n                        rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), yellowAngle);\n                        angledX = rotatedPoint.X;\n                        angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.Y / 255f) * multiplier;\n                            graphicsYellow.FillEllipse(yellowBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n\n                        // Keyline \n                        rotatedPoint = RotatePoint(new Point(x, y), new Point(0, 0), keylineAngle);\n                        angledX = rotatedPoint.X;\n                        angledY = rotatedPoint.Y;\n                        if (rectangle.Contains(new Point(angledX, angledY)))\n                        {\n                            color = sourceBitmap.GetPixel(angledX, angledY);\n                            cmykColor = color;\n                            brushWidth = diameter * (cmykColor.K / 255f) * multiplier;\n\n                            // Just using blck is too dark. \n                            keylineBrush = new SolidBrush(CmykColor.FromCmykColor(0, 0, 0, cmykColor.K));\n                            graphicsKeyline.FillEllipse(keylineBrush, angledX, angledY, brushWidth, brushWidth);\n                        }\n                    }\n                }\n            }\n\n            // Set our white background.\n            using (Graphics graphics = Graphics.FromImage(newImage))\n            {\n                graphics.Clear(Color.White);\n            }\n\n            // Blend the colors now to mimic adaptive blending.\n            using (FastBitmap cyanBitmap = new FastBitmap(cyan))\n            using (FastBitmap magentaBitmap = new FastBitmap(magenta))\n            using (FastBitmap yellowBitmap = new FastBitmap(yellow))\n            using (FastBitmap keylineBitmap = new FastBitmap(keyline))\n            using (FastBitmap destinationBitmap = new FastBitmap(newImage))\n            {\n                Parallel.For(\n                    0,\n                    height,\n                    y =>\n                    {\n                        for (int x = 0; x < width; x++)\n                        {\n                            // ReSharper disable AccessToDisposedClosure\n                            Color cyanPixel = cyanBitmap.GetPixel(x, y);\n                            Color magentaPixel = magentaBitmap.GetPixel(x, y);\n                            Color yellowPixel = yellowBitmap.GetPixel(x, y);\n                            Color keylinePixel = keylineBitmap.GetPixel(x, y);\n\n                            CmykColor blended = cyanPixel.AddAsCmykColor(magentaPixel, yellowPixel, keylinePixel);\n                            destinationBitmap.SetPixel(x, y, blended);\n                            // ReSharper restore AccessToDisposedClosure\n                        }\n                    });\n            }\n        }\n\n        cyan.Dispose();\n        magenta.Dispose();\n        yellow.Dispose();\n        keyline.Dispose();\n        image.Dispose();\n        image = newImage;\n    }\n    catch (Exception ex)\n    {\n        if (cyan != null)\n        {\n            cyan.Dispose();\n        }\n\n        if (magenta != null)\n        {\n            magenta.Dispose();\n        }\n\n        if (yellow != null)\n        {\n            yellow.Dispose();\n        }\n\n        if (keyline != null)\n        {\n            keyline.Dispose();\n        }\n\n        if (newImage != null)\n        {\n            newImage.Dispose();\n        }\n\n        throw new ImageProcessingException("Error processing image with " + this.GetType().Name, ex);\n    }\n\n    return image;\n} \n
Run Code Online (Sandbox Code Playgroud)\n