dcv
dcv copied to clipboard
Is it's possible to remove background with dcv?
I have got a lot of images in jpg format with black background. Is it's possible to find background and remove it (make transparent) with current version of DCV? PNG as output would be enough for me (in ideally wepb, but D do not have native encoder/decoder :( )
If not DCV, maybe there is any better tools for this task?
There are no background extraction methods implemented, such as this, but seems like it would be pretty trivial to remove black background in the example image you've given with DCV.
I cannot test it out at the moment, but I believe basic thresholding would work:
Image image = imread("your_image.png");
auto threshValue = 10; // some low value, experiment on this...
auto alpha = image
.sliced[0 .. $, 0 .. $, 1] // take green channel (most probably will work fine)
.threshold!ubyte(10); // pixel with value below the 10 will be 0, above will be 255.
// if your image is RGB, convert to RGBA and apply alpha
Image rgba = new Image(image.width, image.height, ImageFormat.IF_RGBA, BitDepth.BD_8);
rbga.sliced[0 .. $, 0 .. $, 0 .. 3][] = image.sliced[]; // copy pixel values
rgba.sliced[0 .. $, 0 .. $, 3][] = alpha[]; // copy alpha values
Note that threshValue
can be adjusted - I only gave it value of 10 as a guess, but you can sample your background, and use values more close to it. Also, you can experiment with ranged threshold, where you give lower and upper threshold value.
This is basic idea I would use to solve your problem. Hope I'm of any help, and please let me know if there's anything other that I may help you with.
(in ideally wepb, but D do not have native encoder/decoder :( )
Regarding this matter, my plan is to bind C libraries for image i/o, so if there's a nice wepb C library out there, we should support this format in future.
Hey, one more note- since there's a certain amount of color channel shift in the example image, I suppose taking green channel for thresholding might not work well (it would most probably cut out blue and red regions on borders). Consider using rgb2gray with Rgb2GrayConvertion.MEAN
, in the case green channel does not give you desirable results.
Just found some time, and wanted to try out the solution I proposed above. I got following result (changed only the threshValue
, and used rgb2gray
):
As said, you may tweak params a bit, but that's about it. Hope this solves your problem.
Cheers!
@ljubobratovicrelja very cool! Big thanks! I will try it when finish other work. Could you add this code to example? I think a lot of people have similar task.
Later I will test it on this image:
Could you add this code to example?
Sure! Thanks for suggestion.
Well, this next example image looks bit more challenging. It has lot of underexposed areas in its foreground. I'm quite sure thresholding method won't work that well- you'd have holes in those dark areas in the middle. But you could try using closing morphological operation iteratively (with smaller box kernel, say with size 5) to stitch them up:
// ... thesholding code shown above
foreach(iter; 0 .. iterations) // chose number of iterations empirically (easiest solution)
{
alpha = alpha.close(boxKernel!ubyte(5, 5));
}
Again, I'm can't test this out at the moment, but will do later today.
Well, seems like the closing op won't do much good either. Here is the alpha result from only threshold on the second image:
And, here is with closing:
In cases like this, maybe taking a convex hull of all white regions would solve the case- it's the first thing that comes to mind. You can also try with segmentation algorithms like watershed, but since those dark regions are so large, I'm afraid you'd get similar artifacts. Do these images maybe come in 16bit? It would help significantly if you could use 16 bit, over 8 bit for this task.
Btw, here is the code I've been getting these results with:
Image image = imread("5c9e2f14-5ee4-11e6-961c-eb8198d09c21.jpg");
auto alpha = image
.sliced
.rgb2gray(emptySlice!(2, ubyte), Rgb2GrayConvertion.MEAN)
.asType!float
.conv(gaussian!float(3, 5, 5))
.threshold!ubyte(1); // pixel with value below the 10 will be 0, above will be 255.
alpha.imwrite(ImageFormat.IF_MONO, "alpha1.png");
alpha = alpha.close(radialKernel!ubyte(51));
alpha.imwrite(ImageFormat.IF_MONO, "alpha2.png");
// apply alpha
Image rgba = new Image(image.width, image.height, ImageFormat.IF_RGB_ALPHA, BitDepth.BD_8);
rgba.sliced[0 .. $, 0 .. $, 0 .. 3][] = image.sliced[]; // copy pixel values
rgba.sliced[0 .. $, 0 .. $, 3][] = alpha[]; // copy alpha values
rgba.imwrite("im.png");