How do I convert a Color Image to Black and White using ImageMagick? - .net-core

Background: I performed an Indexed conversion in Gimp of a color image and the result was a nice B&W version of the source Image.
I have tried numerous options in ImageMagick to no avail. I can get close but never quite as clear and crisp as what gimp seems to do effortlessly.
Here is my source:
var bmp = new MagickImage(sourceImage);
bmp.Threshold(new ImageMagick.Percentage(60));
bmp.Resample(200, 200);
bmp.ColorType = ColorType.Bilevel;
bmp.BitDepth(1);
bmp.Settings.Compression = CompressionMethod.Group4;
bmp.Strip();
bmp.Format = MagickFormat.Tiff;
I have been adjusting the Threshold call and have tried various suggestions I have seen online with varying amounts of success.
magickimage has a feature called -monochrome but I have not found how that is achieved in the .net library.
I am sure this is possible but what is the best way to achieve a nice B&W conversion.

With ImageMagick 7, you can do Otsu thresholding.
Input:
magick check.png -alpha off -auto-threshold otsu x.png
Result:
There is no built-in equivalent in ImageMagick 6. However I have a script, otsuthresh that will do that At my web site
So in ImageMagick 6, you just have to do simple thresholding.
convert check.png -alpha off -threshold 50% y.png
Result:
I note that your input image has an opaque alpha channel which needs to be removed to get proper results.

This is possibly the simplest way you can use Greyscale in Magick.NET.
MagickImage image = new MagickImage(imagePath);
image.Grayscale();
string fileName = image.FileName + "_grey.png";
image.Write(fileName);
Instead of image.FileName you can also directly use the image path if you have it.
Optionally you can add a PixelIntensityMethod in Greyscale for eventually better results.
Don't forget to call image.RePage() when you want to crop the image.

Otsu's method will perform better when dealing with texts over a background with slightly gradient colors:
How do I convert a Color Image to Black and White using ImageMagick?
According to #fmw42 's comparison on multiple threshold methods:
the Local Adaptive(its equivalent in imagemagick is -lat) algorithm might work best on texts over background:
Also try to combine with connected components processing that was introduced in another answer made by #fmw42 to remove unnecessary edges/dots which might get confused by any further OCR process.

Related

Adafruit MagTag: Dither a jpg from placekitten.com and display on MagTag

Pimoroni has an example that grabs a jpg online and displays it on one of their Inky e-ink displays.
I am wondering if it is possible to do the same thing with an Adafruit MagTag using CircuitPython. In the latest version of CircuitPython, there are BitmapTools that seem like they may be able to convert a .jpg file into a .bmp that is dithered appropriately for an e-ink display. There just doesn't seem to be any example code that shows how to do this.
Is it possible?
Any examples of using bitmaptools.dither(dest_bitmap: displayio.Bitmap, source_bitmapp: displayio.Bitmap, source_colorspace: displayio.Colorspace, algorithm: DitherAlgorithm = DitherAlgorithm.Atkinson)→ None
The code would look a little like this ...
import board
import displayio
import adafruit_imageload
display = board.DISPLAY
bitmap, palette = adafruit_imageload.load("/purple.bmp",
bitmap=displayio.Bitmap,
palette=displayio.Palette)
# Create a TileGrid to hold the bitmap
tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)
# Create a Group to hold the TileGrid
group = displayio.Group()
# Add the TileGrid to the Group
group.append(tile_grid)
# Add the Group to the Display
display.show(group)
# Loop forever so you can enjoy your image
while True:
pass
... but we would pull in a .jpg and have to do a little work on it so that the greyscale image becomes a dithered .bmp.
Any ideas?
I can pull in a .jpg. I just can't process it and display.
I was also looking for an API or something that might be able to pre-process the image from https://placekitten.com so that it would already be a dithered bitmap file. Does that exist?

bokeh: How to export a grid to png with given size?

I prepared some bokeh plots to display as html.
To this end I prepared a gridplot containing the subplots, the legends and some headings. This all displays extremely nice in HTML and with sizing_mode='stretch_width' it's even kind of responsive.
webpage = gridplot([[header_col],[panel_grid_plot]], toolbar_location=None, sizing_mode='stretch_width')
show(webpage)
Now I also want to export this "webpage" to a PNG. To this end, I use
export_png(webpage, filename= png_filename, width=1800)
Unfortunately, the width parameter is ignored as the webpage is an object of type gridbox and not of type Plot. (This is checked in the bokeh/io/export.py in the method def get_layout_html())
The output is a png of a width of 800px which is kind of useless as the actual information is crushed (while the legends are nicely scaled):
Any ideas how to set the width of my PNG export to useful values?
Is there a way to convert a gridboxto a Plot?
Thanx!
You should've received a warning saying that the width argument will be ignored since you're passing into export_png something that's not a plot.
A way of achieving what you want:
webpage = gridplot(..., sizing_mode='stretch_width')
webpage.width = 1800
export_png(webpage)

Can transparency be used with PostScript/EPS?

I am trying to save an R plot as an EPS file but I have a problem with the following component of the plot - the gray transparent polygon (transparent black = gray effect):
polygon(x.polygon, y.polygon.6, col="#00000022", border=NA)
This line of code works fine when saving the plot as PDF but not as EPS. Looks like EPS does not support transparency? What other choice would I have?
Here is the code for the full plot:
postscript(file="Figure.eps", width=5.5, height=5.5, onefile=F, horizontal=F)
ts(t(data.frame(initial_timepoint, second_timepoint, third_timepoint, final_timepoint)))->obj
obj[,-c(3,7)]->obj1
plot(obj1, plot.type="single", lwd=0.6, xaxs="i",yaxs="i",xlab="",ylab="LV ejection fraction (%)",xaxt='n',yaxt='n',ylim=c(0,70),col="black")
axis(1, at=c(1,2,3,4), labels=c("1","2","3","4"),cex.axis=1)
axis(2, at=seq(0,70,10), labels=c("0%","10%","20%","30%","40%","50%","60%","70%"),cex.axis=1, las=1)
abline(v=c(2,3),lwd=0.6,lty=2)
stderr <- function(x) sqrt(var(x,na.rm=TRUE)/length(na.omit(x)))
avg<-c(mean(initial_timepoint,na.rm=T), mean(second_timepoint,na.rm=T), mean(third_timepoint,na.rm=T), mean(final_timepoint,na.rm=T))
err<-c(stderr(initial_timepoint), stderr(second_timepoint), stderr(third_timepoint), stderr(final_timepoint))
my.count <- c(1,2,3,4)
my.count.rev <- c(4,3,2,1)
y.polygon.6 <- c((avg+err*1.96)[my.count],(avg-err*1.96)[my.count.rev])
x.polygon <- c(my.count, my.count.rev)
polygon(x.polygon, y.polygon.6, col="#00000022", border=NA)
lines(avg,col="black",lwd=0.8,lty=3)
lines((avg+err*1.96),lwd=0.8,lty=3)
lines((avg-err*1.96),lwd=0.8,lty=3)
dev.off()
Although the EPS format does not natively support semi-transparency, it is still possible to use cairo_ps(), that one automatically rasterizes semi-transparent areas, and the resolution at which it does this can be controlled with the argument fallback_resolution :
cairo_ps(file = "test.eps", onefile = FALSE, fallback_resolution = 600)
qplot(Sepal.Length, Petal.Length, data = iris, color = Species, size = Petal.Width, alpha = I(0.7))
dev.off()
All the non-semi-transparent areas then nicely stay as vector graphics.
Or even shorter you can also use :
ggsave("filename.eps", device=cairo_ps, fallback_resolution = 600)
Or use the functions to export to eps using the new export package, which just came out on CRAN :
install.packages("export")
library(export)
graph2eps("filename.eps", fallback_resolution = 600)
That package also supports a number of other export formats, including Powerpoint (graph2ppt), see ?graph2vector, which also retains semi-transparency...
The PostScript graphics model itself does not support general transparency of page elements at all. (Hence it is also not possible for EPS.) PostScript colors are all fully opaque.
An object drawn on top of another object would overwrite and cover all lower objects with its own color leaving no room for transparent effects. (If you see something that looks like transparency overlays in a PostScript viewer or printout, then that was only emulated transparency, by flattening the two (or more) respective objects into one single rasterized area creating the illusion of transparency.)
The PDF graphics model is based on PostScript's, but it extends it in various aspects, adding several new features. One of these is real transparency for complete objects.
After Adobe added transparency to PDF, it also created an extension [1] to the existing PostScript language that was able to include code in PS programs which would add transparency to PDFs created from this PostScript via Distiller. However, when rendering on screen or printing on paper this same original PostScript including this same code, that additional transparency would not appear, and the top (transparent in PDF) object would still overwrite the bottom ones when directly used in PostScript.
What other choice would I have?
Various:
Use PDF only. Don't use EPS.
If you must use EPS, use a two-step process:
Create the PDF first.
Then convert from the (transparency-enabled) PDF to EPS, 'flattening' the transparent elements into rasterized areas which emulate the desired transparency effect.
[1] The name of this extension is called pdfmark. With the help of the pdfmark operator one can also add other features to PostScript code which only materialize when distilling this PostScript to PDF: annotations, interactive form fields and buttons, metadata, hyperlinks, and more. All these elements would not have any effect in the direct PostScript rendering on screen or on paper prints.
Instead of making gray out of transparent black, I recommend using the gray.colors() function in R to generate the shades of gray you need. Then you get the look you want in your .eps file without a problem.
This is working fine for me to save .eps files
import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.use('PS')
legend = plt.legend(loc="upper left", edgecolor="black")
legend.get_frame().set_alpha(None)
legend.get_frame().set_facecolor((0, 0, 0, 0))
plt.show()
plt.savefig('fig1.eps', format='eps')

R: Bitmap output in PDF

A lot of the time, I find it very useful to output graphics with pdf() as it allows me to scroll through pages and observe subtle differences (e.g. the page numbers may correspond to a particular parameter in a simulation).
Sometimes if the plot is quite packed with information, the fact that the PDF is a vector graphic means that it takes a long time to load in a PDF reader and is useless for scrolling through pages. I could plot with png(), but this would result in many image files.
My ideal solution would be to have a device that will plot a bitmap graphic (e.g. PNG) to a PDF.
I have read that cairo_pdf() outputs to a bitmap sometimes? Or I could write something that outputs to PNG, then combines these all together into a PDF?
Any other thoughts? Or does anyone have a solution for this already?
UPDATE: have now added method based on readPNG() as suggested in comments above. It's a bit slower (3s vs 9s) and seems to result in slightly larger file sizes than ImageMagick. rasterImage() interpolation makes no difference to filesize or timing, but alters the appearance slightly. If it's FALSE, then it looks the same as ImageMagick
I have just come up with the following solution using ImageMagick. It's not perfect, but it seems to work well so far.
png2pdf <- function(name=NULL,removepngs=TRUE,method="imagemagick",pnginterpolate=FALSE){
# Run the png() function with a filename of the form name%03d.png
# Then the actual plotting functions, e.g. plot(), lines() etc.
# Then dev.off()
# Then run png2pdf() and specify the name= argument if other pngs exist in the directory
# Need to incorporate a way of dealing with non-square plots
if(is.null(name)){
names <- list.files(pattern="[.]png")
name <- unique(sub("[0-9][0-9][0-9][.]png","",names))
if(length(name)!=1) stop("png2pdf() error: Check filenames")
}else{
names <- list.files(pattern=paste0(name,"[0-9][0-9][0-9][.]png"))
}
# Can change this to "convert" if it is correctly in the system path
if(method=="imagemagick"){
cmd <- c('C:\\Program Files\\ImageMagick-6.9.0-Q16\\convert.exe',names,paste0(name,".pdf"))
system2(cmd[1],cmd[-1])
}else if(method=="readPNG"){
library(png)
pdf(paste0(name,".pdf"))
par(mar=rep(0,4))
for(i in 1:length(names)){
plot(c(0,1),c(0,1),type="n")
rasterImage(readPNG(names[i]),0,0,1,1,interpolate=pnginterpolate)
}
dev.off()
}
if(removepngs) file.remove(names)
}

Resizing gif image causes loss of quality

I'm using Graphics.DrawImage() to resize images in my ASP.NET application.
This is working fine with all image formats, except gif.
Here is my original image:
When I resize to 300px it returns:
The code I'm using is:
Dim postedFile as new Bitmap("flower.gif")
Dim bmpOut As System.Drawing.Bitmap
Dim Format As ImageFormat = postedFile.RawFormat
bmpOut = New Bitmap(width, height)
Dim g As Graphics = Graphics.FromImage(bmpOut)
g.InterpolationMode = InterpolationMode.High
g.DrawImage(postedFile, 0, 0, width, height)
postedFile.Dispose()
Return bmpOut
bmpOut.Dispose()
I've also tried using all of the InterpolationMode's available, including InterpolationMode.HighQualityBicubic, but the image quality is still just as poor.
Why is this happening, and how can I prevent the image quality loss?
The problem is not in the resizing code you posted. I ran using the large image you posted and the result looks great. Your problem arises when you save your newly created 24 bits-per-pixel image to a new gif - with is 8bpp. You have basically two options:
Implement code to produce an optimized color palette for the new gif
(or maybe just use the palette from the original image)
Save to .png instead - which is a completely superior format
If you are saving the resized picture as .gif, I see no reason why this can't happen in the saved .gif. .gif's are limited to 256 colors (unless some hacks are done) and your resized imaged after all the manipulations may have well more than 256. Also, I wouldn't be surprised to find out that whatever routine you're using to convert images to .gif's isn't very concerned about quality.
This is not about interpolation, it is because ColorPalette is not set for target image. You have to quantize. See example at http://glennjones.net/2005/10/high-quality-dynamically-resized-images-with-dot-net/

Resources