Maybe something like this:

img = Import["https://i.sstatic.net/0Jl54.png"]

text = TextRecognize @ img

del = {"the worst", "of times"};

Rasterize[#, RasterSize -> 1000, ImageSize -> ImageDimensions @ img]& @
   Style[#, FontFamily -> "Times"]& @
 StringReplace[
  text, {del[[1]] -> StringPadRight["", StringLength @ del[[1]]], 
   del[[2]] -> StringPadRight["", StringLength @ del[[2]]]}]

Answer from corey979 on Stack Exchange
Top answer
1 of 2
59

PLEASE NOTE

There is a reason why watermarks are there in the first place - to prevent unauthorised reuse of images. For more information on the issues around removing watermarks from images, this Wikimedia article is just one of many useful resources.

The answer below is intended as an exercise in automatically removing text from an image using the inpainting technique. The first part of this answer assumes an unknown text overlay using binarization. The second part of the answer attempts to deal with a known overlay image but unknown position, using image correlation.


Part 1 - Unknown overlay text, unknown position

For scenarios where the text is a given colour that differs a lot from the rest of the image, this is a job for ChanVeseBinarize[]. It even works fairly well with translucent text - here's an example with a bit of translucent white text, using a Mathematica test image:

image = Import["https://i.sstatic.net/LyJTe.png"]

(* This is where parameters become important *)
binimg = ChanVeseBinarize[image, White, {Automatic, 0.12}, MaxIterations -> 1000];

(* Now we can create the mask *)
maskimg = Dilation[DeleteSmallComponents[binimg, 4], 2.5];

And finally the inpainting, making use of the method options to improve the result. It introduces a few artifacts due to an imperfect mask (for example, look at the red edge of the nose), but it's not bad given the text was translucent to start off with. With fully-white text, it's much better.

Inpaint[image, maskimg, Method -> {"TextureSynthesis", "MaxSamples" -> 1200}, MaxIterations -> 500]


Part 2 - Known overlay text, unknown position

This deals with a known piece of overlay text, and makes use of ImageCorrelate[]. Here I make no change to the size or orientation of the overlay, but if you look in the documentation of ImageCorrelate[] under "Generalizations and Extensions" you'll see an example of finding rotated occurrences of a template.

(* The test image is ExampleData[{"TestImage", "Mandrill"}] *)

overlay = Graphics@Text[Style["I love stackexchange", FontFamily -> "Arial", 
    FontSize -> 32, Bold, White, Opacity[0.8]]];

(* Or you can just import an image I've already prepared *)
compositeimage = Import["https://i.sstatic.net/DJEWm.png"]

Then we use ImageCorrelate[] to find the most likely position for the text in the image, and MaxDetect to extract its position (the white dot).

corrimage = ImageAdjust@ImageCorrelate[compositeimage, Binarize@overlay];
maxdetect = MaxDetect[ImageAdjust@DistanceTransform@Binarize[corrimage, 0.9]];
maxpos = Flatten@PixelValuePositions[maxdetect, 1];

(* This combines the overlay into a mask at the correct position *)
blankimage = Image@ConstantArray[0, ImageDimensions@compositeimage];
maskimage = ImageCompose[blankimage, Binarize@overlay, maxpos];
(* Dilation important to ensure full coverage of the mask *)
maskimage = Dilation[maskimage, 2];

Finally we can construct a mask from this information and inpaint. Here the performance is much better than above, because the mask is better.

Inpaint[compositeimage, maskimage, 
 Method -> {"TextureSynthesis", "MaxSamples" -> 1200}, 
 MaxIterations -> 500]

2 of 2
12

Here's an alternative approach that uses the functionality of ImageAlign[] to remove the watermark.

pic = ExampleData[{"TestImage", "Lena"}];
watertext = 
 Image@Graphics@
   Text[Style["Welcome to China", Blue, 30, FontFamily -> "Arial"]]

waterimage = 
 Show[pic, 
   Graphics@
    Text[Style["Welcome to China", Blue, 30, 
      FontFamily -> "Arial"], {130, 400}]] // Image

ali = ImageAlign[waterimage, watertext, 
   TransformationClass -> "Perspective"];
ImageCompose[ali, {waterimage, 0.4}]

mask = ali // ChanVeseBinarize[#, Blue] & // Dilation[#, 3] &;
Inpaint[waterimage, mask, Method -> "TextureSynthesis"]

Discussions

Erase parts of image or overlay with different color - Mathematica Stack Exchange
Is there a way of erasing part of an image? I have been looking for a tool that would enable me to do this. Is there one? Here is a simple minimum working example. I make an image that has some unw... More on mathematica.stackexchange.com
🌐 mathematica.stackexchange.com
May 21, 2021
Mathematical morphology: removing text features from image, while keeping connectivity - Mathematica Stack Exchange
Stack Exchange network consists of 183 Q&A communities including Stack Overflow, the largest, most trusted online community for developers to learn, share their knowledge, and build their careers · Stack Overflow for Teams is now called Stack Internal. Bring the best of human thought and AI ... More on mathematica.stackexchange.com
🌐 mathematica.stackexchange.com
Remove object from the image - Mathematica Stack Exchange
How to remove spider from "spider web"? there is good example in documentation center but how to make black marker of spider? More on mathematica.stackexchange.com
🌐 mathematica.stackexchange.com
April 20, 2016
image processing - Identifying and removing features from scanned handwriten text - Mathematica Stack Exchange
Please, can I have some ideas and help on how to identify and remove features like this one or this one from scanned handwritten text like this: These features are not connected to other text More on mathematica.stackexchange.com
🌐 mathematica.stackexchange.com
June 23, 2016
🌐
Wolfram
wolfram.com › mathematica › newin7 › content › BuiltInImageProcessing › RemoveTextFromAnImage.html
Remove Text from an Image
A bottom-hat transform is used to detect the dark text in the image so its pixels can be replaced with suitable colors.
🌐
Stack Exchange
mathematica.stackexchange.com › questions › 246410 › erase-parts-of-image-or-overlay-with-different-color
Erase parts of image or overlay with different color - Mathematica Stack Exchange
May 21, 2021 - pts1 = RandomReal[{1, 1.2}, {500, 2}]; pts2 = RandomReal[{-0.5, -0.57}, {500, 2}]; g = Graphics[{Point[pts1], Point[pts2], Red, Thickness[0.02], Circle[{0, 0}, 1]}]; gr = Rasterize[g, ImageResolution -> 100]; gr · Now I can use the mask tool to identify the areas I wish to remove
🌐
Wolfram Language
reference.wolfram.com › language › ref › DeleteSmallComponents.html
DeleteSmallComponents: Delete small segments in images or label matrices—Wolfram Documentation
DeleteSmallComponents[image] replaces small connected components in a binary image image with background pixels. DeleteSmallComponents[m] replaces positive integers in a label matrix m with 0 if their tally is small.
🌐
Wolfram Language
reference.wolfram.com › language › ref › RemoveBackground.html
RemoveBackground: Image Foreground-Background Separation—Wolfram Documentation
If an image already has an alpha channel, the result will have an alpha channel equal to the computed foreground map multiplied with the original alpha channel. RemoveBackground for some models uses machine learning. Its methods, training sets and biases included therein may change and yield varied results in different versions of the Wolfram Language. RemoveBackground may download resources that will be stored in your local object store at $LocalBase, and can be listed using LocalObjects[] and removed using ResourceRemove.
Find elsewhere
Top answer
1 of 3
13

Almost!

Convert your image to binary image

img = Import["https://i.sstatic.net/uRUEw.jpg"];
binimg = ColorNegate@Binarize[img, .75]

Get tha mask used in Inpaint

mask = SelectComponents[binimg, {"PerimeterLength", "Elongation"}, 
  60 < # < 160 || #2 > 0.8 &]

Contrasting your image with after.

Grid[{{"Before", Magnify[img, 5]}, {"After",Magnify[Inpaint[img, Dilation[mask, 1], 
     Method -> "TextureSynthesis"], 5]}}]

2 of 3
3

Hey, go to upvote @yode's answer! He's brilliant!

Result first, I hope that would be good enough:

The idea is quite simple------Find those feature and try to delete them.


How to use the code?

  1. Manually select the features you would like to delete, binarize it properly and put it in ker. For example, at this place you could use something like this:

  1. If you want a better result, create another mask and roughly single out all the words. Create a mask like this and put it in msk1:

  1. run the code and get the result.

Code

Inpaint[imgo, 
 Binarize[ColorSeparate[
   HighlightImage[
    imgo, {Red, Opacity@1, 
     Disk[#, 20] & /@ 
      ImageCorners@
       Binarize[
        ImageCorrelate[
         ImageMultiply[ColorNegate@Binarize@imgo, ColorNegate@msk1], 
         ker, Mean@Abs@Flatten[#1 - #2] &], .2]}], "R"], .95]]

How it works

  1. Firstly and most importantly, it will do a ImageCorrelate and find the points with good fit to ker.

  2. Then it will find out those points in need of delete process by using ImageCorners

  3. Finally, we can use Inpaint to get a desired result.


Places that can be further improved

  1. The sign MUST BE almost EXACTLY the same. longer or shorter will influence the detection process and some extra mark in detection area will also interfere with result. Also, sometimes it will mis-detect the down part of "o" if you don't make the mask carefully.

  2. Speed is quite slow......

Hope this can help you~


Edit 1

This edit's main(maybe I should not say "main", it totally is!) contributor is @yode. Without his help, this code will still need a stupid mask.......

Now this answer can correct the image at higher accuracy and have higher detection rate.

The result is like this~

This code requires only imgo and ker:

Inpaint[imgo, 
 Binarize[ColorSeparate[
   HighlightImage[
    imgo, {Red, Opacity@1, 
     Disk[#, 20] & /@ 
      ImageCorners@
       Binarize[
        ImageCorrelate[
         SelectComponents[ColorNegate@Binarize@imgo, 
          "PerimeterLength", 40 < # < 200 &], ker, 
         Mean@Abs@Flatten[#1 - #2] &], .2]}], "R"], .95]]

BTW: @yode is on his way to post a much more elegant, faster and wider appliable answer.

🌐
Wolfram Language
reference.wolfram.com › language › ref › ImageTrim.html
ImageTrim: Crop to a region of interest—Wolfram Documentation
By default, if fewer elements exist than are requested, ImageTrim returns the elements that are present. By setting the Padding option to anything else, pixels outside the original image can also be included.
Top answer
1 of 1
2

I worked with some contrived data and came up with this. I believe the general approach will work with your actual data even some of details need to changed.

Contrived data

data1 = Table[x^1.35 + 50, {x, 0, 33}];
data2 = Table[x^1.35, {x, 0, 33}]; 
places1 =
  Placed[
   {"1", "2", "4", "8", "16", "32"}, 
   Table[{x, Log[x^1.35 + 50]} // N, {x, Subdivide[2, 28, 5]}]];
places2 =
  Placed[
    {"1\!\(\*SubscriptBox[\(T\), \(2\)]\)", 
     Style["2\!\(\*SubscriptBox[\(T\), \(2\)]\)", Background -> Lighter[Gray, .6]], 
     "3\!\(\*SubscriptBox[\(T\), \(2\)]\)", 
     "4\!\(\*SubscriptBox[\(T\), \(2\)]\)", 
     "1\!\(\*SubscriptBox[\(T\), \(1\)]\)", 
     "5\!\(\*SubscriptBox[\(T\), \(2\)]\)", 
     "6\!\(\*SubscriptBox[\(T\), \(2\)]\)", 
     "7\!\(\*SubscriptBox[\(T\), \(2\)]\)", 
     "8\!\(\*SubscriptBox[\(T\), \(2\)]\)", 
     "9\!\(\*SubscriptBox[\(T\), \(2\)]\)"}, 
    N[{#[[1]], Log @ #[[2]]}] & /@ 
     {{3, 1.5}, {5.8, 5}, {9, 12}, {12, 20}, {14, 25}, {16, 32}, 
      {19, 45}, {22, 60}, {25, 90}, {28, 100}}];

The important thing to notice in the above is that because the plot data is going to log scaled, the y-components of label coordinates also have to be log scaled.

Given the properly scaled placements, the plotting can be done with the fairly simple code that follows:

Show[
  ListLinePlot[data1,
    ScalingFunctions -> "Log",
    PlotRange -> {{0, 30}, {0, 200}},
    Frame -> True,
    FrameTicks -> Automatic, 
    FrameLabel -> 
      {Style["T", 14], Style["(E+1/3)\!\(\*SuperscriptBox[\(n\), \(2\)]\)", 14]},
    PlotLabels -> places1],
  ListLinePlot[data2,
    ScalingFunctions -> "Log",
    PlotStyle -> Dashed,
    PlotRange -> {{0, 30}, {0, 200}},
    PlotLabels -> places2],
 ImageSize -> Large]

[

Note that none of labels, except for $2T_2$, has a background

🌐
Wolfram Language
reference.wolfram.com › language › ref › Inpaint.html
Inpaint: Reconstruct missing or unwanted regions—Wolfram Documentation
Wolfram Language code: Inpaint[[image], [image], Method -> {"TextureSynthesis", Masking -> [image]}] The "TotalVariation" method usually gives a better reconstruction of sharp edges: Wolfram Language code: img = [image];Inpaint[img, Dilation[Binarize[img, {0.7, 1}], 1], Method -> #, MaxIterations -> 200]& /@ {"Diffusion", "TotalVariation"} ... Wolfram Language code: im = [image]; Inpaint[im, Dilation[Binarize[ColorSeparate[im][[1]], 0.93], 1]] ... Wolfram Language code: removeSP[img_] := Inpaint[img, ImageAdd[MaxDetect[img], MinDetect[img]], Method -> "Diffusion"]; removeSP[[image]]
🌐
Wolfram Community
community.wolfram.com › groups › - › m › t › 907476
Manipulation and removal of Morphological Components from an Image? - Online Technical Discussion Groups—Wolfram Community
Wolfram Community forum discussion about Manipulation and removal of Morphological Components from an Image?. Stay on top of important topics and build connections by joining Wolfram Community groups relevant to your interests.
🌐
Stack Exchange
mathematica.stackexchange.com › questions › 166884 › ignoring-text-to-compute-regions-in-image
Newest Questions - Mathematica Stack Exchange
Stack Exchange network consists of 184 Q&A communities including Stack Overflow, the largest, most trusted online community for developers to learn, share their knowledge, and build their careers · Stack Overflow for Teams is now called Stack Internal. Bring the best of human thought and AI ...
Top answer
1 of 9
48

This function implements the reverse blend described by Mark Ransom, for an additional small but visible improvement:

reverseBlend[img_Image, alpha_Image, bgcolor_] :=
 With[
  {c = ImageData[img], 
   a = ImageData[alpha] + 0.0001, (* this is to minimize ComplexInfinitys and considerably improve performance *)
   bc = bgcolor},

  ImageClip@
   Image[Quiet[(c - bc (1 - a))/a, {Power::infy, 
       Infinity::indet}] /. {ComplexInfinity -> 0, Indeterminate -> 0}]
  ]

This is the background removal function. The threshold parameter is used for the initial binarization of the image, the minSizeCorrection is for tweaking the size limit of small junk components to be removed after binarization.

removeWhiteBackground[img_, threshold_: 0.05, minSizeCorrection_: 1] :=
  Module[
  {dim, bigmask, mask, edgemask, alpha},
  dim = ImageDimensions[img];
  bigmask = 
   DeleteSmallComponents[
    ColorNegate@
     MorphologicalBinarize[ColorNegate@ImageResize[img, 4 dim], threshold], 
    Round[minSizeCorrection Times @@ dim/5]];
  mask = ColorNegate@
    ImageResize[ColorConvert[bigmask, "GrayScale"], dim];
  edgemask = 
   ImageResize[
    ImageAdjust@DistanceTransform@Dilation[EdgeDetect[bigmask, 2], 6],
     dim];
  alpha = 
   ImageAdd[
    ImageSubtract[
     ImageMultiply[ColorNegate@ColorConvert[img, "GrayScale"], 
      edgemask], ImageMultiply[mask, edgemask]], mask];
  SetAlphaChannel[reverseBlend[img, alpha, 1], alpha]
  ]

Testing the function:

img = Import["https://i.sstatic.net/k7E1F.png"];

background = 
  ImageCrop[
   Import["http://cdn.zmescience.com/wp-content/uploads/2011/06/\
forest2.jpg"], ImageDimensions[img]];

result = removeWhiteBackground[img]

ImageCompose[background, result]
Rasterize[result, Background -> Red]
Rasterize[result, Background -> Black]

Brief explanation of how it works:

  1. Choose your favourite binarization method that produces relatively precise sharp edges

  2. Apply it to an up-scaled image, then downscale the obtained mask to the original size. This gives us antialiasing. Most of the work is done.

  3. For a small improvement, blend the image onto the background using the brightness of its negative as alpha, then blend the obtained image over the original in a thin region around the edges (edgemask) to reduce the visibility of white pixels on the edges. The alpha channel corresponding to these operations is calculated (the somewhat cryptic ImageMultiply/Add expression).

  4. Now we have an estimate of the alpha channel so we can do a reverse blend.

Steps 3 & 4 don't improve that much, but the difference is visible.

2 of 9
45

Perhaps, depending on the edge quality you need:

img = Import@"https://i.sstatic.net/k7E1F.png";
mask = ChanVeseBinarize[img, TargetColor -> {1., 1., 1.}, "LengthPenalty" -> 10]
mask1 = Blur[Erosion[ColorNegate[mask], 2], 5]
Rasterize[SetAlphaChannel[img, mask1], Background -> None]

Edit

Stealing a bit from @Szabolcs

img2 = Import@"https://i.sstatic.net/k7E1F.png";
(*key point:scale up image to smooth the edges*)
img = ImageResize[img2, 4 ImageDimensions[img2]];
mask = ChanVeseBinarize[img, TargetColor -> {1., 1., 1.}, "LengthPenalty" -> 10];
mask1 = Blur[Erosion[ColorNegate[mask], 8], 10];
f[col_] := Rasterize[SetAlphaChannel[img, mask1], Background -> col, 
                     ImageSize -> ImageDimensions@img2]
GraphicsGrid[{{f@Red, f@Blue, f@Green}}]

Click to enlarge

Edit 2

Just to get an idea of the extent of the halo and background imperfections in the image:

img = Import@"https://i.sstatic.net/k7E1F.png";
Join[{img}, MapThread[Binarize, {ColorSeparate[img, "HSB"], {.01, .01, .99}}]]

ColorNegate@ImageAdd[EntropyFilter[img, 1] // ImageAdjust, ColorNegate@img]

🌐
Wolfram Language
reference.wolfram.com › language › ref › ImageCrop.html
ImageCrop: Crop to a size—Wolfram Documentation
ImageCrop[image,{width,height}] by default removes pixels symmetrically from image, or if width or height is larger than for image, it adds pixels symmetrically.