How to process each pixel of the image maximally quickly?

Magick.NET is an object-oriented C# interface to ImageMagick. Use this forum to discuss, make suggestions about, or report bugs concerning Magick.NET
IvanShuvin
Posts: 5
Joined: 2015-06-05T01:31:15-07:00
Authentication code: 6789

How to process each pixel of the image maximally quickly?

Post by IvanShuvin »

I need to precess each pixel on image:

Code: Select all

        MagickImage img = new MagickImage(@"d:\TEST\110706M01000509.jpg");
        PixelCollection pc = img.GetReadOnlyPixels(0, 0, img.Width, img.Height);

        for (int x = 0; x < pc.Width; x++)
            for(int y = 0; y < pc.Height; y++)
            {
                byte[] color = pc.GetValue(x,y);

                //SOME ACTIONS
            }
But, access to the color of pixels of the image(12000 X 16000) takes more than 5 minutes... How more quickly process all pixels of the image?
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: How to process each pixel of the image maximally quickly?

Post by snibgo »

It may be much faster if you reverse x and y, so you process in rows, not columns.
snibgo's IM pages: im.snibgo.com
IvanShuvin
Posts: 5
Joined: 2015-06-05T01:31:15-07:00
Authentication code: 6789

Re: How to process each pixel of the image maximally quickly?

Post by IvanShuvin »

If the first loop by x, the second loop by y, I received next result:
start load -> 14:51:34
start loop -> 14:51:46
end loop -> 14:58:53


If the first loop by y, the second loop by x, I received next result:
start load -> 15:05:56
start loop -> 15:06:08
end loop -> 15:12:56


Improve absent. :(

Anymore ideas?
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: How to process each pixel of the image maximally quickly?

Post by magick »

The slow down comes from accessing pixels from disk, one pixel at a time, in 1 thread. Try accessing one row @ a time:

Code: Select all

        MagickImage img = new MagickImage(@"d:\TEST\110706M01000509.jpg");

        for (int y = 0; x < pc.Height; y++)
        {
            PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);
            for(int x = 0; y < pc.Width; x++)
            {
               byte[] color = pc.GetValue(x,0);

                //SOME ACTIONS
            }
Does that help? You can get some timing information if you set the MAGICK_DEBUG environment variable to 'Cache'.
IvanShuvin
Posts: 5
Joined: 2015-06-05T01:31:15-07:00
Authentication code: 6789

Re: How to process each pixel of the image maximally quickly?

Post by IvanShuvin »

Does that help?
No, I modified my code:

Code: Select all

            MagickImage img = new MagickImage(@"d:\TEST\brightness\tiles\32-1-470-109-25.tif");

            for (int y = 0; y < img.Height; y++)
            {
                PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);

                for (int x = 0; x < pc.Width; x++)
                {
                    byte[] color = pc.GetValue(x, 0);

                    //SOME ACTIONS
                }
            }
I received next result:
start load -> 16:28:38
start loop -> 16:28:40
end loop -> 16:36:13


Improve absent. :( :(
Maybe You have any other ideas?
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: How to process each pixel of the image maximally quickly?

Post by magick »

Let's take .NET out of the equation. How long does this command take:
  • convert 2-1-470-109-25.tif -negate null:
That reads the TIFF image and passes over the pixels one row at a time, there is no output. It took 4.5 seconds on our Linux system (12000x16000 pixel image).
User avatar
dlemstra
Posts: 1570
Joined: 2013-05-04T15:28:54-07:00
Authentication code: 6789
Contact:

Re: How to process each pixel of the image maximally quickly?

Post by dlemstra »

What are you doing in '//SOME ACTIONS'? How quick is it when you don't do anything there?
.NET + ImageMagick = Magick.NET https://github.com/dlemstra/Magick.NET, @MagickNET, Donate
IvanShuvin
Posts: 5
Joined: 2015-06-05T01:31:15-07:00
Authentication code: 6789

Re: How to process each pixel of the image maximally quickly?

Post by IvanShuvin »

What are you doing in '//SOME ACTIONS'?
Nothing. Source code run as is.
I found one the solve:

Code: Select all

            MagickImage img = new MagickImage(@"d:\TEST\brightness\tiles\32-1-470-109-25.tif");
            byte[] color = new byte[3];

            for (int y = 0; y < img.Height; y++)
            {
                PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);
                int chanelsCount = pc.Channels;
                var pcv = pc.GetValues();

                for (int x = 0; x < pcv.Length; x+=chanelsCount)
                {
                    color[0] = pcv[x];
                    color[1] += pcv[x + 1];
                    color[2] += pcv[x + 2];

                    //SOME ACTIONS
                }
            }
Result:
start load -> 20:05:54
start loop -> 20:05:59
end loop -> 20:06:06


But, the function .GetValues() in the version 7.0.0.0014 return only red channel, in the version 6.8.9.601 all correct.
pablobhz
Posts: 69
Joined: 2016-05-07T06:47:14-07:00
Authentication code: 1151

Re: How to process each pixel of the image maximally quickly?

Post by pablobhz »

Sorry to revive this topic, but , any of you implemented this on the latest MagickNET release ? I'm getting a outofmemory exception.

Here's what i tried to do - i tried to adapt his original solution:

Code: Select all

        private static decimal inkGrayLevel(MagickImage img, bool inPercent = true)
        {
            decimal totalPixelValue = 0;
            decimal totalPixelValues = (img.Width * img.Height) * 255;
            //byte[] color = new byte[3];
            ushort[] color = new ushort[3];


                 PixelCollection pc = img.GetPixels();
                //PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);
                int chanelsCount = pc.Channels;
                var pcv = pc.GetValues();

                for (int x = 0; x < pcv.Length; x += chanelsCount)
                {
                    if (color[0] != color[1] || color[1] != color[2] || color[2] != color[3])
                    {
                        throw new ArgumentOutOfRangeException("Image is not grayscale");
                    }
                    color[0] = pcv[x];
                    color[1] += pcv[x + 1];
                    color[2] += pcv[x + 2];
                    totalPixelValue += color[0];

                //SOME ACTIONS
            }
            return (1 - (totalPixelValue / totalPixelValues)) * (inPercent ? 100 : 1);

        }
Thanks in advance
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: How to process each pixel of the image maximally quickly?

Post by snibgo »

What version of IM?

You declare array color to have 3 elements but reference elements 0,1,2 and 3.

What are the values of relevant variables? Eg image width and height?
snibgo's IM pages: im.snibgo.com
pablobhz
Posts: 69
Joined: 2016-05-07T06:47:14-07:00
Authentication code: 1151

Re: How to process each pixel of the image maximally quickly?

Post by pablobhz »

Image Width: 461
Image Height: 7370
Yeah, i just noted that i declared the array with the wrong size. But this wouldn't affect my issue, i think.

What i want is a fast way to scan all image pixels using ImageMagick. The traditional way using System.Bitmap takes a minute or more for every image(and i'm scanning at least 20 of those).
User avatar
dlemstra
Posts: 1570
Joined: 2013-05-04T15:28:54-07:00
Authentication code: 6789
Contact:

Re: How to process each pixel of the image maximally quickly?

Post by dlemstra »

What is the goal of your scan action? It might be possible that you could do it with a build in operation.
.NET + ImageMagick = Magick.NET https://github.com/dlemstra/Magick.NET, @MagickNET, Donate
pablobhz
Posts: 69
Joined: 2016-05-07T06:47:14-07:00
Authentication code: 1151

Re: How to process each pixel of the image maximally quickly?

Post by pablobhz »

I need to scan each pixel of the image in order to get the BitMap Gray level.

In the end, i created a function to do the job:

Code: Select all

        private static decimal GetGrayLevel(Bitmap input, bool inPercent = true)
        {
            decimal totalPixelValue = 0;
            decimal totalPixelValues = (input.Width * input.Height) * 255;

            for (int iy = 0; iy < input.Height; iy++)
            {
                for (int i = 0; i < input.Width; i++)
                {
                    Color clr = input.GetPixel(i, iy);
                    if (clr.R != clr.G || clr.R != clr.B || clr.B != clr.G)
                    {
                        throw new ArgumentOutOfRangeException("Image is not grayscale");
                    }
                    totalPixelValue += clr.R;
                }
            }
            return (1 - (totalPixelValue / totalPixelValues)) * (inPercent ? 100 : 1);
        }
However, since i'm scanning 23 images (461w x 7370h), it takes something like 30 seconds to do the job. And i want to reduce this time.
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: How to process each pixel of the image maximally quickly?

Post by snibgo »

I don't use Magick.NET, but I notice you use "input.GetPixel(i, iy)" at every pixel.

Above, IvanShuvin found a much quicker method was to use "var pcv = pc.GetValues();" at each y, then simply access elements of the pcv array at each x. So the call to ImageMagick is made once per row, instead of once per pixel.
snibgo's IM pages: im.snibgo.com
pablobhz
Posts: 69
Joined: 2016-05-07T06:47:14-07:00
Authentication code: 1151

Re: How to process each pixel of the image maximally quickly?

Post by pablobhz »

Just tried IvanShuvin solution.

Code: Select all

        private static decimal inkGrayLevel(MagickImage img, bool inPercent = true)
        {
            decimal totalPixelValue = 0;
            decimal totalPixelValues = (img.Width * img.Height) * 255;
            int[] color = new int[3];
            for(int y=0; y < img.Height; y++)
            {
                PixelCollection pc = img.GetPixels();
                int channelsCount = pc.Channels;
                var pcv = pc.GetValues();
                for(int x = 0; x < pcv.Length; x += channelsCount)
                {
                    color[0] = (pcv[x]/255) -2;
                    color[1] += (pcv[x + 1]/255)-2;
                    color[2] += (pcv[x + 2]/255)-2;
                    

                   // if (color[0] != color[1] || color[0] != color[2] || color[2] != color[1])
                   // {
                  //      throw new ArgumentOutOfRangeException("Image is not grayscale");
                  //  }
                    totalPixelValue += color[0];
                } 
            }  
            return (1 - (totalPixelValue / totalPixelValues)) * (inPercent ? 100 : 1);
        }
To be honest, it is taking longer than my previous solution.
Doing those operations on a image 460w x 7370h. One minute now and going so far...
Also, i had to remove the conditional to detect if image wasn't grayscale. Sometimes pixels would return 510 on their values (after dividing by 255).
I can remove my division, since i only need to compare if they're different. However, i think this will have influence on my final results.
Post Reply