如何在 py-opencv 中保存 dpi 信息?
How to save dpi info in py-opencv?
import cv2
def clear(img):
back = cv2.imread("back.png", cv2.IMREAD_GRAYSCALE)
img = cv2.bitwise_xor(img, back)
ret, img = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY_INV)
return img
def threshold(img):
ret, img = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY_INV)
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
ret, img = cv2.threshold(img, 248, 255, cv2.THRESH_BINARY)
return img
def fomatImage(img):
img = threshold(img)
img = clear(img)
return img
img = fomatImage(cv2.imread("1566135246468.png",cv2.IMREAD_COLOR))
cv2.imwrite("aa.png",img)
这是我的代码。但是当我试图用 tesseract-ocr 识别它时,我得到了一个警告。
Warning: Invalid resolution 0 dpi. Using 70 instead.
我应该如何设置dpi?
AFAIK,OpenCV 没有设置它写入的 PNG 文件的 dpi
,因此您正在寻找解决方法。这里有一些想法...
方法 1 - 使用 PIL/Pillow 而不是 OpenCV
PIL/Pillow可以将dpi
信息写入PNG文件。所以你会:
第 1 步 - 将 BGR OpenCV 图像转换为 RGB 以匹配 PIL 的通道顺序
from PIL import Image
RGBimage = cv2.cvtColor(BGRimage, cv2.COLOR_BGR2RGB)
步骤 2 - 将 OpenCV Numpy 数组转换为 PIL 图像
PILimage = Image.fromarray(RGBimage)
步骤 3 - 使用 PIL 写入
PILimage.save('result.png', dpi=(72,72))
正如 Fred 在评论中提到的那样,您同样可以以几乎相同的方式使用 Python Wand。
方法 2 - 用 OpenCV 编写,然后用一些工具修改
您可以使用 Python 的 subprocess
模块来 shell 输出到 ImageMagick 并像这样设置 dpi:
magick OpenCVImage.png -set units pixelspercentimeter -density 28.3 result.png
您只需要知道 PNG 使用公制(每厘米点数)而不是英制(每英寸点数)并且一英寸有 2.54 厘米,因此 72 dpi 变为每厘米 28.3 点。
如果您的 ImageMagick 版本早于 v7,请将 magick
替换为 convert
。
方法三——用OpenCV编写,自己插入dpi
您可以使用 OpenCV 的 imencode()
将您的文件写入内存。然后在文件中搜索 IDAT
(图像数据)块 - 这是包含图像像素的块,并在设置密度的块之前插入一个 pHYs
块。然后写入磁盘。
实际上并没有那么难 - 它只有 9 个字节,请参阅 here 并查看答案末尾的 pngcheck
输出。
此代码未经生产测试,但对我来说似乎工作得很好:
#!/usr/bin/env python3
import struct
import numpy as np
import cv2
import zlib
def writePNGwithdpi(im, filename, dpi=(72,72)):
"""Save the image as PNG with embedded dpi"""
# Encode as PNG into memory
retval, buffer = cv2.imencode(".png", im)
s = buffer.tostring()
# Find start of IDAT chunk
IDAToffset = s.find(b'IDAT') - 4
# Create our lovely new pHYs chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11pHYs
pHYs = b'pHYs' + struct.pack('!IIc',int(dpi[0]/0.0254),int(dpi[1]/0.0254),b"\x01" )
pHYs = struct.pack('!I',9) + pHYs + struct.pack('!I',zlib.crc32(pHYs))
# Open output filename and write...
# ... stuff preceding IDAT as created by OpenCV
# ... new pHYs as created by us above
# ... IDAT onwards as created by OpenCV
with open(filename, "wb") as out:
out.write(buffer[0:IDAToffset])
out.write(pHYs)
out.write(buffer[IDAToffset:])
################################################################################
# main
################################################################################
# Load sample image
im = cv2.imread('lena.png')
# Save at specific dpi
writePNGwithdpi(im, "result.png", (32,300))
无论你使用哪种方法,你都可以使用pngcheck --v image.png
来检查你做了什么:
pngcheck -vv a.png
示例输出
File: a.png (306 bytes)
chunk IHDR at offset 0x0000c, length 13
100 x 100 image, 1-bit palette, non-interlaced
chunk gAMA at offset 0x00025, length 4: 0.45455
chunk cHRM at offset 0x00035, length 32
White x = 0.3127 y = 0.329, Red x = 0.64 y = 0.33
Green x = 0.3 y = 0.6, Blue x = 0.15 y = 0.06
chunk PLTE at offset 0x00061, length 6: 2 palette entries
chunk bKGD at offset 0x00073, length 1
index = 1
chunk pHYs at offset 0x00080, length 9: 255x255 pixels/unit (1:1). <-- THIS SETS THE DENSITY
chunk tIME at offset 0x00095, length 7: 19 Aug 2019 10:15:00 UTC
chunk IDAT at offset 0x000a8, length 20
zlib: deflated, 2K window, maximum compression
row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(100 out of 100)
chunk tEXt at offset 0x000c8, length 37, keyword: date:create
chunk tEXt at offset 0x000f9, length 37, keyword: date:modify
chunk IEND at offset 0x0012a, length 0
No errors detected in a.png (11 chunks, 76.5% compression).
在编辑 PNG 块时,我还设法与作者一起设置了一个时间块和一个文本块。他们是这样的:
# Create a new tIME chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11tIME
year, month, day, hour, min, sec = 2020, 12, 25, 12, 0, 0 # Midday Christmas day 2020
tIME = b'tIME' + struct.pack('!HBBBBB',year,month,day,hour,min,sec)
tIME = struct.pack('!I',7) + tIME + struct.pack('!I',zlib.crc32(tIME))
# Create a new tEXt chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11tEXt
Author = "Author\x00Sir Mark The Great"
tEXt = b'tEXt' + bytes(Author.encode('ascii'))
tEXt = struct.pack('!I',len(Author)) + tEXt + struct.pack('!I',zlib.crc32(tEXt))
# Open output filename and write...
# ... stuff preceding IDAT as created by OpenCV
# ... new pHYs as created by us above
# ... new tIME as created by us above
# ... new tEXt as created by us above
# ... IDAT onwards as created by OpenCV
with open(filename, "wb") as out:
out.write(buffer[0:IDAToffset])
out.write(pHYs)
out.write(tIME)
out.write(tEXt)
out.write(buffer[IDAToffset:])
关键词: OpenCV, PIL, Pillow, dpi, density, imwrite, PNG, chunks, pHYs chunk, Python, image, image-processing, tEXt chunk , 时间块, 作者, 评论
import cv2
def clear(img):
back = cv2.imread("back.png", cv2.IMREAD_GRAYSCALE)
img = cv2.bitwise_xor(img, back)
ret, img = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY_INV)
return img
def threshold(img):
ret, img = cv2.threshold(img, 120, 255, cv2.THRESH_BINARY_INV)
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
ret, img = cv2.threshold(img, 248, 255, cv2.THRESH_BINARY)
return img
def fomatImage(img):
img = threshold(img)
img = clear(img)
return img
img = fomatImage(cv2.imread("1566135246468.png",cv2.IMREAD_COLOR))
cv2.imwrite("aa.png",img)
这是我的代码。但是当我试图用 tesseract-ocr 识别它时,我得到了一个警告。
Warning: Invalid resolution 0 dpi. Using 70 instead.
我应该如何设置dpi?
AFAIK,OpenCV 没有设置它写入的 PNG 文件的 dpi
,因此您正在寻找解决方法。这里有一些想法...
方法 1 - 使用 PIL/Pillow 而不是 OpenCV
PIL/Pillow可以将dpi
信息写入PNG文件。所以你会:
第 1 步 - 将 BGR OpenCV 图像转换为 RGB 以匹配 PIL 的通道顺序
from PIL import Image
RGBimage = cv2.cvtColor(BGRimage, cv2.COLOR_BGR2RGB)
步骤 2 - 将 OpenCV Numpy 数组转换为 PIL 图像
PILimage = Image.fromarray(RGBimage)
步骤 3 - 使用 PIL 写入
PILimage.save('result.png', dpi=(72,72))
正如 Fred 在评论中提到的那样,您同样可以以几乎相同的方式使用 Python Wand。
方法 2 - 用 OpenCV 编写,然后用一些工具修改
您可以使用 Python 的 subprocess
模块来 shell 输出到 ImageMagick 并像这样设置 dpi:
magick OpenCVImage.png -set units pixelspercentimeter -density 28.3 result.png
您只需要知道 PNG 使用公制(每厘米点数)而不是英制(每英寸点数)并且一英寸有 2.54 厘米,因此 72 dpi 变为每厘米 28.3 点。
如果您的 ImageMagick 版本早于 v7,请将 magick
替换为 convert
。
方法三——用OpenCV编写,自己插入dpi
您可以使用 OpenCV 的 imencode()
将您的文件写入内存。然后在文件中搜索 IDAT
(图像数据)块 - 这是包含图像像素的块,并在设置密度的块之前插入一个 pHYs
块。然后写入磁盘。
实际上并没有那么难 - 它只有 9 个字节,请参阅 here 并查看答案末尾的 pngcheck
输出。
此代码未经生产测试,但对我来说似乎工作得很好:
#!/usr/bin/env python3
import struct
import numpy as np
import cv2
import zlib
def writePNGwithdpi(im, filename, dpi=(72,72)):
"""Save the image as PNG with embedded dpi"""
# Encode as PNG into memory
retval, buffer = cv2.imencode(".png", im)
s = buffer.tostring()
# Find start of IDAT chunk
IDAToffset = s.find(b'IDAT') - 4
# Create our lovely new pHYs chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11pHYs
pHYs = b'pHYs' + struct.pack('!IIc',int(dpi[0]/0.0254),int(dpi[1]/0.0254),b"\x01" )
pHYs = struct.pack('!I',9) + pHYs + struct.pack('!I',zlib.crc32(pHYs))
# Open output filename and write...
# ... stuff preceding IDAT as created by OpenCV
# ... new pHYs as created by us above
# ... IDAT onwards as created by OpenCV
with open(filename, "wb") as out:
out.write(buffer[0:IDAToffset])
out.write(pHYs)
out.write(buffer[IDAToffset:])
################################################################################
# main
################################################################################
# Load sample image
im = cv2.imread('lena.png')
# Save at specific dpi
writePNGwithdpi(im, "result.png", (32,300))
无论你使用哪种方法,你都可以使用pngcheck --v image.png
来检查你做了什么:
pngcheck -vv a.png
示例输出
File: a.png (306 bytes)
chunk IHDR at offset 0x0000c, length 13
100 x 100 image, 1-bit palette, non-interlaced
chunk gAMA at offset 0x00025, length 4: 0.45455
chunk cHRM at offset 0x00035, length 32
White x = 0.3127 y = 0.329, Red x = 0.64 y = 0.33
Green x = 0.3 y = 0.6, Blue x = 0.15 y = 0.06
chunk PLTE at offset 0x00061, length 6: 2 palette entries
chunk bKGD at offset 0x00073, length 1
index = 1
chunk pHYs at offset 0x00080, length 9: 255x255 pixels/unit (1:1). <-- THIS SETS THE DENSITY
chunk tIME at offset 0x00095, length 7: 19 Aug 2019 10:15:00 UTC
chunk IDAT at offset 0x000a8, length 20
zlib: deflated, 2K window, maximum compression
row filters (0 none, 1 sub, 2 up, 3 avg, 4 paeth):
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(100 out of 100)
chunk tEXt at offset 0x000c8, length 37, keyword: date:create
chunk tEXt at offset 0x000f9, length 37, keyword: date:modify
chunk IEND at offset 0x0012a, length 0
No errors detected in a.png (11 chunks, 76.5% compression).
在编辑 PNG 块时,我还设法与作者一起设置了一个时间块和一个文本块。他们是这样的:
# Create a new tIME chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11tIME
year, month, day, hour, min, sec = 2020, 12, 25, 12, 0, 0 # Midday Christmas day 2020
tIME = b'tIME' + struct.pack('!HBBBBB',year,month,day,hour,min,sec)
tIME = struct.pack('!I',7) + tIME + struct.pack('!I',zlib.crc32(tIME))
# Create a new tEXt chunk - https://www.w3.org/TR/2003/REC-PNG-20031110/#11tEXt
Author = "Author\x00Sir Mark The Great"
tEXt = b'tEXt' + bytes(Author.encode('ascii'))
tEXt = struct.pack('!I',len(Author)) + tEXt + struct.pack('!I',zlib.crc32(tEXt))
# Open output filename and write...
# ... stuff preceding IDAT as created by OpenCV
# ... new pHYs as created by us above
# ... new tIME as created by us above
# ... new tEXt as created by us above
# ... IDAT onwards as created by OpenCV
with open(filename, "wb") as out:
out.write(buffer[0:IDAToffset])
out.write(pHYs)
out.write(tIME)
out.write(tEXt)
out.write(buffer[IDAToffset:])
关键词: OpenCV, PIL, Pillow, dpi, density, imwrite, PNG, chunks, pHYs chunk, Python, image, image-processing, tEXt chunk , 时间块, 作者, 评论