Remove text from jpeg

╄→гoц情女王★ 提交于 2021-02-07 10:13:18

问题


I have a jpeg containing alpha blended text. Knowing the font and size, I deduced a png file representing the text

Using ImageMagick, can I get an approximation of the original picture?


回答1:


Albert Myšák has the proper technique here, since one knows the exact alpha channel values for the text and the equation that describes how the alpha channel is blended with the image. Kudos!

My earlier methods are better suited to when one only knows the positions of the text pixels in the image, so that one can make a binary mask or convert the text to transparency or some other color needed by whatever inpainting software is used.

Here is the equivalent one line Imagemagick command broken into several continuation lines for easier reading and explaining.

Line1 - read the kitty image
Line2 - copy it and make it all white rgb(255,255,255), save it into memory and delete the copy image from the image sequence
Line3 - read the watermark image and extract the alpha channel. Then subtract it from white
Line4 - divide the result of line 3 by the white image
Line5 - divide the kitty image by the result of line 4
Line6 - save the result to disk

convert kitty.png \
\( -clone 0 -fill white -colorize 100 -write mpr:white +delete \) \
\( watermark.png -alpha extract mpr:white -compose minus -composite \
mpr:white +swap -compose divide -composite \) \
+swap -compose divide -composite \
kitty_restored.png





回答2:


One way to do this is with a technique called inpainting. You can find that in (Python) Skimage at

http://scikit-image.org/docs/dev/api/skimage.restoration.html#inpaint-biharmonic

or in OpenCV at

https://docs.opencv.org/3.0-beta/modules/photo/doc/inpainting.html https://docs.opencv.org/3.4.0/df/d3d/tutorial_py_inpainting.html

Here is the Python Skimage inpainting processing:

kitty image:

watermark image:

The Skimage inpainting requires a binary mask image. So I can convert your watermark to such a mask by:

convert watermark.png -alpha extract -threshold 0 mask.png


mask image:

Here is the Python code:

#!/opt/local/bin/python3.6

import numpy as np
import skimage.io
import skimage.restoration
import skimage.exposure

img = skimage.io.imread('/Users/fred/desktop/kitty.png')
msk = skimage.io.imread('/Users/fred/desktop/mask.png')
msk = skimage.exposure.rescale_intensity(msk, in_range='image', out_range=(0,1))
newimg = skimage.restoration.inpaint_biharmonic(img, msk, multichannel=True)
skimage.io.imsave('/Users/fred/desktop/kitty_inpaint_biharmonic.png', newimg)


Imagemagick does not have an official version of that. But user snibgo on the Imagemagick forum has implemented a custom version he calls 'hole filling' at http://im.snibgo.com/fillholespri.htm. He shows an example at https://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=28640#p127233.

Furthermore, on the same page, he shows some clever Imagemagick code that does repeated small amounts of resizing. This achieves a somewhat similar result to inpainting. But in general, it will not be quite as good as inpainting. Nevertheless, it does work moderately well for your image.

kitty image:

watermark image:

First, I have to take your watermark image and extract a binary image from it where the text is white and the background black. Then I use it to make the kitty image transparent where the text resides. Then I crop out the area of the text just to make the subsequent processing faster.

convert kitty.png \
\( watermark.png -alpha extract -threshold 0 -negate \) \
-alpha off -compose copy_opacity -composite \
-crop 490x102+235+150 +repage tmp1.png


Then I run his rather long sequence of successive resizing of the image followed by merging all the layers and resizing back to the original size.

convert tmp1.png \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
\( +clone -resize 90.9091% \) \
-layers RemoveDups \
-filter Gaussian -resize 490x102! \
-background None \
-compose DstOver -layers merge \
-alpha opaque \
tmp2.png


Then finally, I composite this result back into the place on the kitty image from which I had cropped it.

convert kitty.png tmp2.png -geometry +235+150 -compose over -composite kitty2.png


At full resolution, you can still make out the very faint text residual in this image. The Skimage result is better as can be seen by rapidly alternating the two images.




回答3:


you can do this in two lines of code.

import numpy as np
import cv2
import matplotlib.pyplot as plt
source_image = cv2.imread("6LfDs.png") #Image of cat with text watermark
text = cv2.imread("gJAAx.png", cv2.IMREAD_UNCHANGED) #Image of text

correcting_matrix = ((255 -text[:,:,3]) /255) #Matrix of "how much this pixel was darkened by applying text overlay"
original_image = (source_image / correcting_matrix[:,:,np.newaxis]).astype(np.uint8) 

cv2.imwrite("original_image.png", original_image)

How does this work? Lets say you have pixel of cat with text with value of 15 and you know that alpha channel of text has value of 64.

If alpha channel has value of 64, we know that original pixel was multiplied by (255-64)/255, which is 0.75. Original value is 15/0.75 = 20

We can do this for every pixel and get original image



来源:https://stackoverflow.com/questions/52461350/remove-text-from-jpeg

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!