I'm working on a tool that cuts a large image into smaller tiles using ImageMagick via Python. And I need all the tiles to be on the same format (png, 8 or 16 bits).
In most case, it works just fine, but on monochromatic tiles ImageMagick compresses the picture on writing the file. For instance, pure black tiles are compressed to a 1 bit picture.
I use the plain save method, as explained in the docs.
I found no documentation about this autocompressing feature nor any way to avoid this.
Is there a workaround for this or a way I can avoid this happening?
edit:
For instance, if I use this code to import a 24bit rgb picture:
from wand.image import Image
img = Image(filename ='http://upload.wikimedia.org/wikipedia/commons/6/68/Solid_black.png')
print img.type
I get this as type
bilevel
if I add this,
img.type = 'grayscale'
print img.type
Once again I get
bilevel
If I try to force the pixel depth like this,
img.depth = 16
print img.type
print img.depth
I get:
bilevel
16
I thought that maybe it actually changed the depth, but once I save the image, it become 1 bit depth again.
So it seems to me that ImageMagick just automatically compresses the picture and that I have no control over it. It even refuses to change the image type.
Any ideas to avoid this? Any way to force the pixel depth?
Related
I'm using Python Wand module(version 0.4.3.) to convert an image stored in pdf to PNG. Final PNG quality is great when I saved the final image in its orignal image width and height. But, when I try to save it to smaller image final PNG gets blurry and quality is not that great.
The difference between two images is shown here. Top image is converted to original size (10800x7200px). The second one is scale to 1250x833px.
Is there any way I can improve the second image? I played with different filter and blur setting.But, could not get the image quality I want. Any help is greatly appreciated.
Code I used to convert PDF to png in its original size:
def pdf_to_png(pdf_name, res):
with Image(filename=pdf_name, resolution=res) as img:
with Image(width=img.width,height=img.height, background=Color("white")) as bg:
bg.composite(img,0,0)`
bg.save(filename="Drawing_improved_wand.png")`
pdf_to_png('Drawing_1.pdf', 300)
Code for resized png:
with Image(filename="Drawing_1.pdf", resolution=(300,300)) as img:
with Image(width=1250, height=833, background=Color("white")) as bg:
img.resize(1250, 833,filter='undefined', blur=1)
img.format = 'png'
bg.composite(img,0,0)
bg.save(filename='Drawing_improved_wand1250x833.png')
This is likely due to an inefficiency with how ImageMagick handles rasterization form PDF text + vectors, and not because of anything you're doing wrong. The large PNG likely has the same problems as the small one, but since the resolution is almost an order of magnitude higher, the effects become imperceptible.
If when exporting to the large PNG the file looks good, I would use this for further processing (like scaling down) and not the PDF.
have you try to set blur < 1?
for example:
img.resize(1250, 833,filter='undefined', blur=0.1)
My overall goal is to crop several regions from an input mirax (.mrxs) slide image to JPEG output files.
Here is what one of these images looks like:
Note that the darker grey area is part of the image, and the regions I ultimately wish to extract in JPEG format are the 3 black square regions.
Now, for the specifics:
I'm able to extract the color channels from the mirax image into 3 separate TIFF files using vips on the command line:
vips extract_band INPUT.mrxs OUTPUT.tiff[tile,compression=jpeg] C --n 1
Where C corresponds to the channel number (0-2), and each output file is about 250 MB in size.
The next job is to somehow recognize and extract the regions of interest from the images, so I turned to several python imaging libraries, and this is where I encountered difficulties.
When I try to load any of the TIFFs using OpenCV using:
i = cv2.imread('/home/user/input_img.tiff',cv2.IMREAD_ANYDEPTH)
I get an error error: (-211) The total matrix size does not fit to "size_t" type in function setSize
I managed to get a little more traction with Pillow, by doing:
from PIL import Image
tiff = Image.open('/home/user/input_img.tiff')
print len(tiff.tile)
print tiff.tile[0]
print tiff.info
which outputs:
636633
('jpeg', (0, 0, 128, 128), 8, ('L', ''))
{'compression': 'jpeg', 'dpi': (25.4, 25.4)}
However, beyond loading the image, I can't seem to perform any useful operations; for example doing tiff.tostring() results in a MemoryError (I do this in an attempt to convert the PIL object to a numpy array) I'm not sure this operation is even valid given the existence of tiles.
From my limited understanding, these TIFFs store the image data in 'tiles' (of which the above image contains 636633) in a JPEG-compressed format.
It's not clear to me, however, how would one would extract these tiles for use as regular JPEG images, or even whether the sequence of steps in the above process I outlined is a potentially useful way of accomplishing the overall goal of extracting the ROIs from the mirax image.
If I'm on the right track, then some guidance would be appreciated, or, if there's another way to accomplish my goal using vips/openslide without python I would be interested in hearing ideas. Additionally, more information about how I could deal with or understand the TIFF files I described would also be helpful.
The ideal situations would include:
1) Some kind of autocropping feature in vips/openslide which can generate JPEGs from either the TIFFs or original mirax image, along the lines of what the following command does, but without generated tens of thousands of images:
vips dzsave CMU-1.mrxs[autocrop] pyramid
2) Being able to extract tiles from the TIFFs and store the data corresponding to the image region as a numpy array in order to detect the 3 ROIs using OpenCV or another methd.
I would use the vips Python binding, it's very like PIL but can handle these huge images. Try something like:
from gi.repository import Vips
slide = Vips.Image.new_from_file(sys.argv[1])
tile = slide.extract_area(left, top, width, height)
tile.write_to_file(sys.argv[2])
You can also extract areas on the command-line, of course:
$ vips extract_area INPUT.mrxs OUTPUT.tiff left top width height
Though that will be a little slower than a loop in Python. You can use crop as a synonym for extract_area.
openslide attaches a lot of metadata to the image describing the layout and position of the various subimages. Try:
$ vipsheader -a myslide.mrxs
And have a look through the output. You might be able to calculate the position of your subimages from that. I would also ask on the openslide mailing list, they are very expert and very helpful.
One more thing you could try: get a low-res overview, corner-detect on that, then extract the tiles from the high-res image. To get a low-res version of your slide, try:
$ vips copy myslide.mrxs[level=7] overview.tif
Level 7 is downsampled by 2 ** 7, so 128x.
I want to write a python code that reads a .jpg picture, alter some of its RBG components and save it again, without changing the picture size.
I tried to load the picture using OpenCV and PyGame, however, when I tried a simple Load/Save code, using three different functions, the resulting images is greater in size than the initial image. This is the code I used.
>>> import cv, pygame # Importing OpenCV & PyGame libraries.
>>> image_opencv = cv.LoadImage('lena.jpg')
>>> image_opencv_matrix = cv.LoadImageM('lena.jpg')
>>> image_pygame = pygame.image.load('lena.jpg')
>>> cv.SaveImage('lena_opencv.jpg', image_opencv)
>>> cv.SaveImage('lena_opencv_matrix.jpg', image_opencv_matrix)
>>> pygame.image.save(image_pygame, 'lena_pygame.jpg')
The original size was 48.3K, and the resulting are 75.5K, 75.5K, 49.9K.
So, I'm not sure I'm missing something that makes the picture original size changes, although I only made a Load/Save, or not?
And is there a better library to use rather than OpenCV or PyGame ?!
JPEG is a lossy image format. When you open and save one, you’re encoding the entire image again. You can adjust the quality settings to approximate the original file size, but you’re going to lose some image quality regardless. There’s no general way to know what the original quality setting was, but if the file size is important, you could guess until you get it close.
The size of a JPEG output depends on 3 things:
The dimensions of the original image. In your case these are the same for all 3 examples.
The color complexity within the image. An image with a lot of detail will be bigger than one that is totally blank.
The quality setting used in the encoder. In your case you used the defaults, which appear to be higher for OpenCV vs. PyGame. A better quality setting will generate a file that's closer to the original (less lossy) but larger.
Because of the lossy nature of JPEG some of this is slightly unpredictable. You can save an image with a particular quality setting, open that new image and save it again at the exact same quality setting, and it will probably be slightly different in size because of the changes introduced when you saved it the first time.
How can I replace a colour across multiple images with another in python? I have a folder with 400 sprite animations. I would like to change the block coloured shadow (111,79,51) with one which has alpha transparencies. I could easily do the batch converting using:
img = glob.glob(filepath\*.bmp)
however I dont know how I could change the pixel colours. If it makes any difference, the images are all 96x96 and i dont care how long the process is. I am using python 3.2.2 so I cant really use PIL (I think)
BMP is a windows file format, so you will need PIL or something like it; or you can roll your own reader/writer. The basic modules won't help as far as I'm aware. You can read PPM and GIF using Tk (PhotoImage()) which is part of the standard distribution and use get() and put() on that image to change pixel values. See references online, because it's not straight-forward - the pixels come from get() as 3-tuple integers, but need to go back to put() as space-separated hex text!
Are your images in indexed mode (8 bit per pixel with a palette),or "truecolor" 32bpp images? If they are in indexed modes, it would not be hard to simply mark the palette entry for that color to be transparent across all files.
Otherwise, you will really have to process all pixel data. It also could be done by writting a Python script for GIMP - but that would require Python-2 nonetheless.
There are a couple similar questions on SO, but none of them really helped. Basically I am trying to resize a simple png image, as seen here:
http://media.spiralknights.com/wiki-images/3/3e/Equipment-Proto_Sword_icon.png
(from the mmo Spiral Knights, copyright Three Rings Entertainment)
I had originally implemented a utility which uses these images in php, and the resizing utility there worked perfectly well. I used the method described on the imagecopyresampled page in PHP's documentation.
Honestly I can't even get it to resize better in Photoshop, but the results are almost disastrous in python. I consistently get a halo effect, and I believe this is at least in part to the actual RGBA values of the transparent pixels. Here, this picture tells it better:
(the second to last resize was just a suggestion I saw on another forum to resize first to twice the final size, and it DID help at least a little, but not enough)
The image is already in RGBA mode when it is being resized.
As you can see the PHP and Photoshop resizes are halo-free. Honestly everything but the PHP resize does TOO much work, I like the minimal palette in the php image (if you look at the larger versions you can see that the PHP resize uses less in between colours), but I could live with the way Photoshop has resized it, or even the inner part of the python resize, but the halo is unacceptable.
It seems to me -- and correct me if I'm wrong -- that PHP and Photoshop seem to know not to use the colour of the pixels in the alpha channel when interpolating, but python is using that light border, which is otherwise transparent, in its resize.
Unfortunately there are a lot of different icons that I need to resize, with varying profiles, so they're not all as simple as this circular one, but this was just the first one I was using while experimenting.
It's not much of a code question in and of itself, but if you need something to look at then this is the gist:
>> import Image
>> img = Image.open('swordorig.png')
>> img
<PngImagePlugin.PngImageFile image mode=RGBA size=256x256 at 0x2A3AF58>
>> img.resize((36,36), Image.ANTIALIAS).save('swordresize.png')
Eventual question being: is there a way to tell PIL NOT to use the colour of a pixel that has an alpha of 0 while resampling?
Thanks to everyone for answering! And especially to rotoglup for telling me the term for what I was actually looking for. Going through the stack overflow pages for premultiplied alpha in PIL I found a solution that works; here is an updated sheet of resize graphics:
The gray bars are just to give an impression of what they would look like on a lighter background.
https://stackoverflow.com/a/6882161/1189554
Thanks to madlag
So my code ends up looking like this:
>> import Image, numpy
>> img = Image.open('swordorig.png')
>> premult = numpy.fromstring(img.tostring(), dtype=numpy.uint8)
>> alphaLayer = premult[3::4] / 255.0
>> premult[::4] *= alphaLayer
>> premult[1::4] *= alphaLayer
>> premult[2::4] *= alphaLayer
>> img = Image.fromstring("RGBA", img.size, premult.tostring())
>> img.resize((36,36), Image.ANTIALIAS).save('swordresize.png')
And you get the icon that is on the bottom of the sheet. Still more colours than the PHP version, and a very mild ring, but overall much, much nicer. I'm sure the algorithm could be tweaked to improve it even more. Thanks again to everyone for responding!