I need to increase the dpi of my image before reading with ocr in opencv. The problems are :
I do not know the dpi of my image right now
I do not know how to increase the dpi of an image
I searched in Google, and almost every answer suggests using cv2.resize
image = cv2.imread("source.png")
resized_image = cv2.resize(image, (100, 50)) #I need to change it to 300 DPI
resize only changes the size of image, but after all does not increase the dpi. I tried to use it, and then checked in Photoshop, the dpi was not changed.
How to do it with opencv?
I need to change dpi to 300, why do I need to know current dpi? Because if it is already dpi > 300, I do not need to convert it.
I do it with python.
The dpi is just a number in the JPEG/TIFF/PNG header. It is entirely irrelevant to the world and his dog until you print the image and then it determines how large the print will be given the image's dimensions in pixels.
During image processing, it is irrelevant. The only thing of any interest is the number of pixels you have. That is the ultimate determinant of image quality, or information content - however you want to describe it.
I don't believe you can set it with OpenCV. You can certainly set it with ImageMagick like this in the Terminal:
mogrify -set density 300 *.png # v6 ImageMagick
magick mogrify -set density 300 *.png # v7 ImageMagick
You can check it with:
identify -format "Density: %x x %y" SomeImage.jpg # v6 ImageMagick
magick identify -format ... as above # v7 ImageMagick
You can do similar things with exiftool in Terminal - note that exiftool is MUCH smaller and easier to maintain than ImageMagick because it is "just" a (very capable) single Perl script:
Extract image resolution from EXIF IFD1 information:
exiftool -IFD1:XResolution -IFD1:YResolution image.jpg
Extract all tags with names containing the word "Resolution" from an image|:
exiftool '-*resolution*' image.jpg
Set X/Y Resolution (density) on image.jpg:
exiftool -xresolution=300 -yresolution=300 image.jpg
Here is a little demonstration of what I mean at the beginning of my answer...
Use ImageMagick to create an image 1024x768 with no dpi information:
convert -size 1024x768 xc:black image.jpg
Now examine it:
identify -verbose image.jpg
Image: image.jpg
Format: JPEG (Joint Photographic Experts Group JFIF format)
Mime type: image/jpeg
Class: PseudoClass
Geometry: 1024x768+0+0
Units: Undefined
Colorspace: Gray
Type: Bilevel
...
...
Now change the dpi and set the dpi units and examine it again:
mogrify -set density 300 -units pixelsperinch image.jpg # Change dpi
identify -verbose image.jpg # Examine
Image: image.jpg
Format: JPEG (Joint Photographic Experts Group JFIF format)
Mime type: image/jpeg
Class: PseudoClass
Geometry: 1024x768+0+0 <--- Number of pixels is unchanged
Resolution: 300x300 <---
Print size: 3.41333x2.56 <--- Print size is now known
Units: PixelsPerInch <---
Colorspace: Gray
Type: Bilevel
...
...
And now you can see that suddenly we know how big a print will come out and that the number of pixels has not changed.
Even though this is an old post I just wanted to say that Tesseract has been tested and found to operate better when the height of the characters is around 30 pixels. Please check the following link:
https://groups.google.com/forum/#!msg/tesseract-ocr/Wdh_JJwnw94/24JHDYQbBQAJ
DPI is inherited property of graphical device - monitor, scanner, camera, etc. For example - lets say that we are scanning image, and we want to get image with better quality - so we set higher DPI value in scanner options. If no better DPI option - so we need to buy a better scanner which supports more scan resolutions. There are devices/methods which are able to achieve 100 000 DPI
Related
my code:
from PIL import Image
img = Image.open('this.jpg') # 2.72mb image
arrayA = np.array(img)
new_img = Image.fromarray(arrayA)
new_img.save("this_changed.jpg") # to 660 kb image size
PIL will compress and subsample your image in order to save space. Try to deactivate compressing and subsamling:
new_image.save("this_changed.jpg",
quality=100, # preserve color and image quality
subsampling=0 # use the whole image as base for the save
)
PIL saves images with a quality of 75 by default, i.e. if you don't say otherwise. If the original image has a quality of 100, and you save it as per your code, it will save it with quality 75 which will result in a smaller file size.
Let's make an example. Here is an image with quality 100:
Let's check its quality and file size with exiftool:
exiftool -filesize -JPEGQualityEstimate -n image.jpg
File Size : 896738
JPEG Quality Estimate : 100
Run your code, and you'll get:
exiftool -filesize -JPEGQualityEstimate -n result.jpg
File Size : 184922
JPEG Quality Estimate : 75
Notice it is lower quality and smaller.
Now tell PIL to keep the quality of the original:
im.save('result.jpg', quality='keep')
and check again:
exiftool -filesize -JPEGQualityEstimate -n result.jpg
File Size : 1261893
JPEG Quality Estimate : 100
Notice the quality setting has been retained and the file is now much larger - we'll come to that in a minute.
Just for completeness, let's tell PIL to use a quality of 85%:
im.save('result.jpg', quality=85)
and check results:
exiftool -filesize -JPEGQualityEstimate -n result.jpg
File Size : 232407
JPEG Quality Estimate : 85
The next question is why the file size is different from the original when specifying 'keep'. That is because JPEG is lossy and different encoders are allowed to make different decisions about how they do chroma-subsampling, what quantisation tables they use, whether they use integer or floating point accuracy and so on to optimise either filesize, or encoding speed or some other factor that was important to the author of the encoder. As such, you might find different Python/C++ packages produce different absolute values for pixels and different file sizes and in fact, these can change from one release/version of a library to the next. If you want your image data to remain identical, you should consider using PNG or another lossless format, e.g. NetPBM, TIFF.
I have a Python script that uses Pillow to resize an image to an Instagram sized image with blurred background from the original image.
Before and after images (both JPGs):
https://app.box.com/s/jpv2mxlncp9871zvx9ygt0be4gf0zc9q
Is this simply a function of the 'after' JPG being too small to reflect all the colors in the original image? (Instagram only allows 2048x2048 images max, my original is a JPG converted from TIF from a 24.2-megapixel RAW image taken from a Nikon DSLR). Perhaps it's all in my head, but in my opinion, the 'after' image has lost some saturation / vibrance (compare the yellow buildings and car lights for example)
Has anyone experienced similar issues? Is there some default mode in Pillow that is reducing the number of colors available? I'm thinking of adding an additional saturation step to my script, but that seems like a hack.
EDIT: Added another before / after pair of images to the link above. I also realize I can easily share the script's source (GitHub repo):
https://github.com/princefishthrower/instagramize
The difference is that the original image contains an "ICC Colour Profile" (amongst others) which are not preserved in the output image.
You can see this most easily with exiftool:
exiftool Mountains_Before.jpg | grep -i profile
Or with ImageMagick:
magick identify -verbose Mountains_Before.jpg | grep -A999 Profiles:
Output
Profiles:
Profile-8bim: 92 bytes
Profile-exif: 17796 bytes
Profile-icc: 560 bytes
Profile-iptc: 80 bytes
City[1,90]: 0x00000000: 254700 -%G
Created Date[2,55]: 2020-7-1
unknown[2,62]: 2020-06-30
unknown[2,63]: 21:11:26+00:00
unknown[2,0]: 4
Created Time[2,60]: 20:22:05-20:22
Profile-xmp: 9701 bytes
If you strip the profiles from the original, you will see it too, is washed out and flatter:
magick Mountains_Before.jpg -strip NoProfile.jpg
You can extract the ICC Profile and look at out like this if that sort of thing excites you:
magick Mountains_Before.jpg profile.icc
If you did that, I guess you could re-attach the profile from the BEFORE image to the AFTER image like this:
magick Mountains_After.jpg -profile profile.icc AfterWithProfile.jpg
Keywords: Image processing, ImageMagick, profile, ICC profile, saturation, saturated, desaturated, washed out.
As Mark Setchell pointed out, it is a matter of preserving the color profile of the image, which is possible natively in Pillow, first by retrieving the profile after opening the image:
image = Image.open('mycoolimage.jpg')
iccProfile = image.info.get('icc_profile')
iccBytes = io.BytesIO(iccProfile)
originalColorProfile = ImageCms.ImageCmsProfile(iccBytes)
and when calling save with Pillow you can pass an icc_profile:
image.save('outputimagename.jpg', icc_profile=originalColorProfile.tobytes())
(Obviously, I am doing other manipulations to image in between these two steps here. Apparently one or more of them cause the icc_profile to disappear.)
This answer was also helpful in building this solution.
I added Mountains_After_NEW.jpg for those interested to see the results of these additions.
Right now, I am running a basic python script from bash that looks like this:
import sys
def main():
sys.stdout.write("hi")
if __name__=="__main__":
main()
When run using python (py justdosomething.py) it works fine. When I try to run it from bash, it gives this massive error message from imagemagick, despite me not importing it in this file:
Version: ImageMagick 6.9.4-1 Q16 x86_64 2016-05-11 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2016 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC Modules
Delegates (built-in): bzlib freetype jng jpeg ltdl lzma png tiff xml zlib
Usage: import [options ...] [ file ]
Image Settings:
-adjoin join images into a single multi-image file
-border include window border in the output image
-channel type apply option to select image channels
-colorspace type alternate image colorspace
-comment string annotate image with comment
-compress type type of pixel compression when writing the image
-define format:option
define one or more image format options
-density geometry horizontal and vertical density of the image
-depth value image depth
-descend obtain image by descending window hierarchy
-display server X server to contact
-dispose method layer disposal method
-dither method apply error diffusion to image
-delay value display the next image after pausing
-encipher filename convert plain pixels to cipher pixels
-endian type endianness (MSB or LSB) of the image
-encoding type text encoding type
-filter type use this filter when resizing an image
-format "string" output formatted image characteristics
-frame include window manager frame
-gravity direction which direction to gravitate towards
-identify identify the format and characteristics of the image
-interlace type None, Line, Plane, or Partition
-interpolate method pixel color interpolation method
-label string assign a label to an image
-limit type value Area, Disk, Map, or Memory resource limit
-monitor monitor progress
-page geometry size and location of an image canvas
-pause seconds seconds delay between snapshots
-pointsize value font point size
-quality value JPEG/MIFF/PNG compression level
-quiet suppress all warning messages
-regard-warnings pay attention to warning messages
-respect-parentheses settings remain in effect until parenthesis boundary
-sampling-factor geometry
horizontal and vertical sampling factor
-scene value image scene number
-screen select image from root window
-seed value seed a new sequence of pseudo-random numbers
-set property value set an image property
-silent operate silently, i.e. don't ring any bells
-snaps value number of screen snapshots
-support factor resize support: > 1.0 is blurry, < 1.0 is sharp
-synchronize synchronize image to storage device
-taint declare the image as modified
-transparent-color color
transparent color
-treedepth value color tree depth
-verbose print detailed information about the image
-virtual-pixel method
Constant, Edge, Mirror, or Tile
-window id select window with this id or name
Image Operators:
-annotate geometry text
annotate the image with text
-colors value preferred number of colors in the image
-crop geometry preferred size and location of the cropped image
-encipher filename convert plain pixels to cipher pixels
-geometry geometry preferred size or location of the image
-help print program options
-monochrome transform image to black and white
-negate replace every pixel with its complementary color
-repage geometry size and location of an image canvas
-quantize colorspace reduce colors in this colorspace
-resize geometry resize the image
-rotate degrees apply Paeth rotation to the image
-strip strip image of all profiles and comments
-thumbnail geometry create a thumbnail of the image
-transparent color make this color transparent within the image
-trim trim image edges
-type type image type
Miscellaneous Options:
-debug events display copious debugging information
-help print program options
-list type print a list of supported option arguments
-log format format of debugging information
-version print version information
By default, 'file' is written in the MIFF image format. To
specify a particular image format, precede the filename with an image
format name and a colon (i.e. ps:image) or specify the image type as
the filename suffix (i.e. image.ps). Specify 'file' as '-' for
standard input or output.
import: delegate library support not built-in `' (X11) # error/import.c/ImportImageCommand/1297.
It then gives me two error messages that seem unrelated:
./justdosomething.py: line 3: syntax error near unexpected token `('
./justdosomething.py: line 3: `def main():'
Why would running it from bash cause a totally unrelated and unused library to proc an error? Why would "def main()" be unrecognizable as a command? I'm lost here.
Add the following as the first line of the script.
#!/usr/bin/env python
Why would running it from bash cause a totally unrelated and unused library to proc an error?
The bash interpreter doesn't understand the Python programming language. Instead, it calls the import utility (provided by ImageMagick).
As sys is not a valid argument for import (the utility) it writes to stderr + basic usage info.
Why would "def main()" be unrecognizable as a command?
Simple. That is Python - not anything Bash will understand. You'll need to invoke the Python run-time to execute python scripts.
I'm trying to convert EPS images to JPEG using Pillow. But the results are of low quality. I'm trying to use resize method, but it gets completely ignored. I set up the size of JPEG image as (3600, 4700), but the resulted image has (360, 470) size. My code is:
eps_image = Image.open('img.eps')
height = eps_image.height * 10
width = eps_image.width * 10
new_size = (height, width)
print(new_size) # prints (3600, 4700)
eps_image.resize(new_size, Image.ANTIALIAS)
eps_image.save(
'img.jpeg',
format='JPEG'
dpi=(9000, 9000),
quality=95)
UPD. Vasu Deo.S noticed one my error, and thanks to him the JPG image has become bigger, but quality is still low. I've tried different DPI, sizes, resample values for resize function, but the result does not change much. How can i make it better?
The problem is that PIL is a raster image processor, as opposed to a vector image processor. It "rasterises" vector images (such as your EPS file and SVG files) onto a grid when it opens them because it can only deal with rasters.
If that grid doesn't have enough resolution, you can never regain it. Normally, it rasterises at 100dpi, so if you want to make bigger images, you need to rasterise onto a larger grid before you even get started.
Compare:
from PIL import Image
eps_image = Image.open('image.eps')
eps_image.save('a.jpg')
The result is 540x720:
And this:
from PIL import Image
eps_image = Image.open('image.eps')
# Rasterise onto 4x higher resolution grid
eps_image.load(scale=4)
eps_image.save('a.jpg')
The result is 2160x2880:
You now have enough quality to resize however you like.
Note that you don't need to write any Python to do this at all - ImageMagick will do it all for you. It is included in most Linux distros and is available for macOS and Windows and you just use it in Terminal. The equivalent command is like this:
magick -density 400 input.eps -resize 800x600 -quality 95 output.jpg
It's because eps_image.resize(new_size, Image.ANTIALIAS) returns an resized copy of an image. Therefore you have to store it in a separate variable. Just change:-
eps_image.resize(new_size, Image.ANTIALIAS)
to
eps_image = eps_image.resize(new_size, Image.ANTIALIAS)
UPDATE:-
These may not solve the problem completely, but still would help.
You are trying to save your output image as a .jpeg, which is a
lossy compression format, therefore information is lost during the
compression/transformation (for the most part). Change the output
file extension to a lossless compression format like .png so that
data would not be compromised during compression. Also change
quality=95 to quality=100 in Image.save()
You are using Image.ANTIALIAS for resampling the image, which is
not that good when upscaling the image (it has been replaced by
Image.LANCZOS in newer version, the clause still exists for
backward compatibility). Try using Image.BICUBIC, which produces
quite favorable results (for the most part) when upscaling the image.
Consider the following MWE to generate a random image:
import matplotlib.pyplot as plt
import numpy as np
pts = np.random.random_sample((1024, 1024))
plt.imsave('foo.png',pts, dpi=300)
I'm trying to understand how the dpi option works. According to the matplotlib.pyplot.imsave documentation,
dpi : int
The DPI to store in the metadata of the file. This does not affect the resolution of the output image.
The output of the program above is a 1024x1024 image file.
What I don't understand is the fact that neither identify -verbose foo.png nor exiftool foo.png shows the image resolution.
But, opening it with ImageMagick (display) and checking the image info, I find
So, what is the math behind the resolution and printing size values?
How to obtain a 300dpi resolution image?
Image formats like png do not have a dpi defined. If you save a 1024 x 1024 pixel array via imsave, the image will simply be 1024 x 1024 pixel.
Imagemagick seems to ignore any metadata, so it assumes a resolution of 96 dpi. From the pixel size (1024) and the dpi (96) it then calculates the size in inches to be
1024 dots / 96 dots per inch = 10.667 inch
That said, the question "How to obtain a 300dpi resolution image?" is not really clear. But most graphics viewers would allow to scale the image prior to printing, so it shouldn't be a problem to get a 300 dpi print on paper.
In ImageMagick, you can set the output density by
convert image <processing> -density 300 newimage
Then to check the density you can do either
identify -verbose newimage
or
identify -format "%xx%y"
to find the density (resolution)