Threshold value when converting image to 1bpp?

2019-06-08 09:12发布

问题:

I don't know how to tag this question, please edit if possible.

The job: Create an application which can auto-crop black borders in images in batch runs. Images vary in quality from 100-300dpi, 1bpp-24bpp and a batch can vary from 10 - 10 000 images.

The plan: Convert image to 1bpp (bitonal, black/white, if it isn't already) and after "cleaning up" white spots/dirt/noise find where the black ends and the white begins, these are the new coords for the image crop, apply them to a clone of the original image. Delete old image, save new one.

The progress: All of the above is done, and works, but...

The problem: When converting to 1bpp I have no control of a "threshold" value. I need this. A lot of dark images get cropped too much.

The tries: I've tried

Bitmap imgBitonal = imgOriginal.Clone(new Rectangle(0, 0, b.Width, b.Height), PixelFormat.Format1bppIndexed)

And also this. Both of which work, but none seem to give me the possibility to manually set a threshold value. I need for the user to be able to set this value, amongst others, and use my "preview" function before running the batch so as to see if the settings are any good.

The cry: I'm at a loss here. I don't now what to do or how to do it. Please help a fellow coder out. Point me in a direction, show me where in the code found in the link a threshold value is found (I haven't found one, or don't know where to look) or just give me some code that works. Any help is appreciated.

回答1:

Try this, from very fast 1bpp convert:

Duplicate from here Convert 24bpp Bitmap to 1bpp

        private static unsafe void Convert(Bitmap src, Bitmap conv)
        {
            // Lock source and destination in memory for unsafe access
            var bmbo = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadOnly,
                                     src.PixelFormat);
            var bmdn = conv.LockBits(new Rectangle(0, 0, conv.Width, conv.Height), ImageLockMode.ReadWrite,
                                     conv.PixelFormat);

            var srcScan0 = bmbo.Scan0;
            var convScan0 = bmdn.Scan0;

            var srcStride = bmbo.Stride;
            var convStride = bmdn.Stride;

            byte* sourcePixels = (byte*)(void*)srcScan0;
            byte* destPixels = (byte*)(void*)convScan0;

            var srcLineIdx = 0;
            var convLineIdx = 0;
            var hmax = src.Height-1;
            var wmax = src.Width-1;
            for (int y = 0; y < hmax; y++)
            {
                // find indexes for source/destination lines

                // use addition, not multiplication?
                srcLineIdx += srcStride;
                convLineIdx += convStride;

                var srcIdx = srcLineIdx;
                for (int x = 0; x < wmax; x++)
                {
                    // index for source pixel (32bbp, rgba format)
                    srcIdx += 4;
                    //var r = pixel[2];
                    //var g = pixel[1];
                    //var b = pixel[0];

                    // could just check directly?
                    //if (Color.FromArgb(r,g,b).GetBrightness() > 0.01f)
                    if (!(sourcePixels[srcIdx] == 0 && sourcePixels[srcIdx + 1] == 0 && sourcePixels[srcIdx + 2] == 0))
                    {
                        // destination byte for pixel (1bpp, ie 8pixels per byte)
                        var idx = convLineIdx + (x >> 3);
                        // mask out pixel bit in destination byte
                        destPixels[idx] |= (byte)(0x80 >> (x & 0x7));
                    }
                }
            }
            src.UnlockBits(bmbo);
            conv.UnlockBits(bmdn);
        }