Faster image processing than bit lock

I am working on a border detection program in C #, and to make it work faster, I recently used it to block bits. However, lockBits are still not as fast as we would like it to start. Although the problem may be my general algorithm, I am also wondering if there is anything better than the lockBits that I can use for image processing.

In case the problem is an algorithm, here is the main explanation. Go through an array of colors (made using locks that represent pixels) and for each color, check the color of the eight pixels around that pixel. If these pixels are not close enough to the current pixel, consider the current pixel on the edge.

Here is the base code that determines if a pixel is an edge. It takes a color [] of nine colors, the first of which is the pixel to check.

public Boolean isEdgeOptimized(Color[] colors) { //colors[0] should be the checking pixel Boolean returnBool = true; float percentage = percentageInt; //the percentage used is set //equal to the global variable percentageInt if (isMatching(colors[0], colors[1], percentage) && isMatching(colors[0], colors[2], percentage) && isMatching(colors[0], colors[3], percentage) && isMatching(colors[0], colors[4], percentage) && isMatching(colors[0], colors[5], percentage) && isMatching(colors[0], colors[6], percentage) && isMatching(colors[0], colors[7], percentage) && isMatching(colors[0], colors[8], percentage)) { returnBool = false; } return returnBool; } 

This code is applied to each pixel whose colors are selected using locks.

So basically the question is, how can I make my program run faster? Is this my algorithm, or is there something I can use, is it faster than lockBits?

By the way, the project is on gitHub, here

+4
source share
4 answers

Are you really passing a floating point number as a percentage to isMatching ?

I looked at your isMatching code on GitHub and well, yikes. You ported it with Java, right? C # uses bool not Boolean , and although I don't know for sure, I don't like the look of the code, which does a lot of boxing and unboxing. In addition, you do a ton of multiplication and floating point comparisons when you don't need:

 public static bool IsMatching(Color a, Color b, int percent) { //this method is used to identify whether two pixels, //of color a and b match, as in they can be considered //a solid color based on the acceptance value (percent) int thresh = (int)(percent * 255); return Math.Abs(aR - bR) < thresh && Math.Abs(aG - bG) < thresh && Math.Abs(aB - bB) < thresh; } 

This will reduce the amount of work you do per pixel. I don't like it anyway, because I try to avoid method calls in the middle of a pixel loop, especially with an 8x pixel loop. I made a static method to cut out an instance passed in that it is not used. Only these changes are likely to double your productivity, since we only do 1 multiplication, without boxing, and now use the inherent short circuit && to shorten the work.

If I were to do this, I would rather do something like this:

 // assert: bitmap.Height > 2 && bitmap.Width > 2 BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); int scaledPercent = percent * 255; unsafe { byte* prevLine = (byte*)data.Scan0; byte* currLine = prevLine + data.Stride; byte* nextLine = currLine + data.Stride; for (int y=1; y < bitmap.Height - 1; y++) { byte* pp = prevLine + 3; byte* cp = currLine + 3; byte* np = nextLine + 3; for (int x = 1; x < bitmap.Width - 1; x++) { if (IsEdgeOptimized(pp, cp, np, scaledPercent)) { // do what you need to do } pp += 3; cp += 3; np += 3; } prevLine = currLine; currLine = nextLine; nextLine += data.Stride; } } private unsafe static bool IsEdgeOptimized(byte* pp, byte* cp, byte* np, int scaledPecent) { return IsMatching(cp, pp - 3, scaledPercent) && IsMatching(cp, pp, scaledPercent) && IsMatching(cp, pp + 3, scaledPercent) && IsMatching(cp, cp - 3, scaledPercent) && IsMatching(cp, cp + 3, scaledPercent) && IsMatching(cp, np - 3, scaledPercent) && IsMatching(cp, np, scaledPercent) && IsMatching(cp, np + 3, scaledPercent); } private unsafe static bool IsMatching(byte* p1, byte* p2, int thresh) { return Math.Abs(p1++ - p2++) < thresh && Math.Abs(p1++ - p2++) < thresh && Math.Abs(p1 - p2) < thresh; } 

Which now does all kinds of horrible pointers to reduce access to the array and so on. If this whole pointer makes you feel uncomfortable, you can allocate byte arrays for prevLine, currLine and nextLine and make a .Copy marshal for each line in the process.

Algorithm: start with one pixel from the top and left and iterate over each pixel of the image, except for the outer edge (without boundary conditions! Yay!). I keep pointers to the beginning of each line, prevLine, currLine and nextLine. Then, when I start the x loop, I compose pp, cp, np, which are the previous pixel, current pixel and the next pixel. the current pixel is really the one we care about. pp - the pixel directly above it, np directly below it. I pass them to IsEdgeOptimized, which scans cp, calling IsMatching for each.

Now all this assumes 24 bits per pixel. If you are looking for 32 bits per pixel, all these magic 3 should be 4 there, but other than that the code does not change. You can parameterize the number of bytes per pixel if you want it to work.

FYI, channels in pixels, usually b, g, r, (a).

Colors are stored as bytes in memory. Your actual bitmap, if it is a 24-bit image, is stored as a block of bytes. The scanned lines have a width of data.Stride bytes, which is at least 3 * the number of pixels per line (it can be more, since scan lines are often supplemented).

When I declare a variable like byte * in C #, I do a few things. First, I say that this variable contains the address of the byte location in memory. Secondly, I say that I am going to break all security measures in .NET, because now I can read and write any byte in memory, which can be dangerous.

So, when I have something like:

 Math.Abs(*p1++ - *p2++) < thresh 

What he says (and it will be a long time):

  • Take the byte that p1 points to and holds it
  • Add 1 to p1 (this is ++ - it points to the next byte pointer)
  • Take the byte that p2 points to and holds it
  • Add 1 to p2
  • Subtract step 3 from step 1
  • Go to Math.Abs .

The rationale for this is that historically reading the contents of a byte and moving forward is a very common operation, and many of them are embedded in a single operation of a pair of instructions that combine into one cycle or so.

When we enter isMatching , p1 points to pixel 1, p2 points to pixel 2, and in memory they are laid out as follows:

 p1 : B p1 + 1: G p1 + 2: R p2 : B p2 + 1: G p2 + 2: R 

So, isMatching just makes the absolute difference when navigating through memory.

Your next question tells me that you really don't understand pointers. This is normal - you can probably study them. Honestly, the concepts are really not that difficult, but the problem with them is that without a lot of experience, you most likely shoot in the foot, and maybe you should think about it just by using the profiling tool for your code and cooling down the worst hot spots and call it good.

For example, you will notice that I look from the first row to the penultimate row and the first column to the penultimate column. This is deliberate in order to avoid the need to handle the case โ€œI canโ€™t read above the 0th lineโ€, which eliminates a large class of potential errors that are associated with reading outside the legal memory block, which can be mild in many conditions of execution.

+5
source

Instead of copying each image to byte[] , then copying it to Color[] , creating another pace Color[9] for each pixel, and then use SetPixel set the color, compile with the /unsafe flag, mark the method as unsafe, replace copying on byte[] on Marshal.Copy on:

 using (byte* bytePtr = ptr) { //code goes here } 

Make sure you replace the SetPixel call with setting the correct bytes. This is not a problem with LockBits, you need LockBits, the problem is that you are ineffective with everything else related to image processing.

+5
source

If you want to use parallel task execution, you can use the Parallel class in the System.Threading.Tasks namespace. The following link contains some examples and explanations.

0
source

You can split the image into 10 raster images and process each of them, and then finally combine them (just an idea).

0
source

All Articles