Skip to main content
dank2882
Participating Frequently
January 30, 2018
Answered

How to remove small black dots from text page - selecting pixel radius via script

  • January 30, 2018
  • 1 reply
  • 8496 views

I have thousands of antique pages of text and some of them have little black dots on them. These are not text layers but rather all the text is a single image layer. I am trying to remove the dots without damaging or altering the text in anyway but I don't have time to do it manually by erasing each dot with the eraser tool. I have tried every tutorial and tool I can find to solve the problem but they all seem to

1. Removes to much of the text I am trying to keep

2. Has to be selected manually one by one

3. Effects the text I am trying to preserve

Here is an example of what I am working with

Notice all the small black dots that don't belong.

Perhaps there is another tool available that I have missed but I can't seem to find one so here is what I am thinking and asking.

If I separate the black text from the white background in the layer is it possible to create a script that can select any parts of the layer/image that are a radius of say 5 pixels or less?

If I could I could then select the small dots and delete them and the main text would be preserved because off of those items will be larger than only 5 pixels.

If there is another tool I don't know about that would work I am open to that, please keep in mind I have thousands of pages so I am looking at running a script or action to accomplish this and not manually selecting all of them.

Thanks for reading this and any help that might follow,

Dan

This topic has been closed for replies.
Correct answer Kukurykus

Sorry for the delay but I wanted to let you know that the script is working amazingly.

Let us know if we can email you small gift,

Dan


Actually I do it for free as I'm enthusiast of Photoshop Scripting, so that was great pleasure to have another task that is other than rest I tried I could find a method for I haven't been honoured by any means yet and haven't supposed I can. Well, if you guys want to be nice for me this way - that is completetly ok if I take some little payback 2nd chance can no happen, someone is so greatful for my work that would like to emphasize this cooperation must also convey some values. I wonder what that is, but whatever it will be I'm happy to recieve it. After all I saved weeks of your time making those texts readable.

For full automation replace last two paragraphs to this code. It's going to let you choose folder which (.psd/.jpg) files (also those nested in all level subfolders) will be processed. Then each saved as .jpg in autocreated subfolder of its parent folder:

function FSf(sF, i) {

     sF = Folder(sF).getFiles()

     for(re = /\.(jpe?g|psd)$/i; i < sF.length; i++) {

          if (sF instanceof Folder) FSf(sF, 0)

          else if(re.test(sF.name)) {

               if (!((fld = Folder(sF.path + '/processed'))).exists) fld.create(); open(sF)

               cR2('shadows'), pat(2.7), SfP('set'), pD(), (sel = (aD = activeDocument).selection).grow(23, true), sel.invert()

               sel.grow(23, true), sel.invert(), cTL(), aD.activeLayer = (lyr = aD.layers)[1], sel.selectAll(), rst(), sel.fill(backgroundColor)

               sel.deselect(), aD.activeLayer = lyr[0],  str(25, 15, 5, 'foregroundColor', 1, 'MULTIPLY', false), aD.flatten()

               cR2('shadows'), BaC(25, 75), sel.contract(1), sel.smooth(1), sel.expand(1), lyr[0].applyUnSharpMask(500, 1000, 250)

               sel.feather(1), sel.grow(25, true), cTL(), BaC(150, 100), aD.selection.fill(backgroundColor, ColorBlendMode.BEHIND)

               lyr[0].applyGaussianBlur(.3), aD.flatten(), aD.saveAs(File(fld + '/' + aD.name), jpg), aD.close()

          } 

     }  

displayDialogs = DialogModes.NO

jpg = new JPEGSaveOptions(); jpg.quality = 12

FSf(Folder.selectDialog('select folder'), 0)

1 reply

Kukurykus
Legend
February 1, 2018

It assumes there is only background in your file, if that has to work. Tests were done only on 648*391 pixels dimension and 72 px/in resolution document. It removes 100% black dots (not beeing 'flying' parts of letters), and doesn't affect scripture, but make it unremarkably thinner & contrastive and still remarkably similar to original Ask me question if you have any.

function sTT(v) {return stringIDToTypeID(v)}

function cR2(v1, v2, v3, v4) {// UNSAMPLED COLOR RANGE:

     eval("(dsc1 = new ActionDescriptor())" + (v1 == 'skinTone' ? ".putInteger(sTT('fuzziness'), v4)" : ""))

     dsc1.putEnumerated(sTT(C = (c = 'color') + 's'), sTT(C), sTT(v1))

     for(i = 0; i < (arr = ['invert', 'UseFacesKey']).length; i++) dsc1.putBoolean(sTT(arr), eval('v' + (i + 2)) || false)

     dsc1.putInteger(sTT(c + 'Model'), 0), executeAction(sTT(c + 'Range'), dsc1, DialogModes.NO)

     // v1: 'radius', 'cyans', 'yellows', 'graininess', 'blues', 'magenta'

     // 'highlights', 'midtones', 'shadows', 'skinTone', 'outOfGamut'

     // v2: boolean; v3: boolean, v4: number (0 - 200)

}

function pat(v) {// PATH FROM SELECTION

     (ref1 = new ActionReference()).putClass(sTT('path'));

     (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1);

     (ref2 = new ActionReference()).putProperty(sTT('selectionClass'), sTT('selection'))

     dsc1.putReference(sTT('from'), ref2)

     dsc1.putUnitDouble(sTT('tolerance'), sTT('pixelsUnit'), v)

     executeAction(sTT('make'), dsc1, DialogModes.NO)

     // v: (.5 - 10)

}

function SfP(v1, v2) {// SSELECTION FROM PATH:

     (ref1 = new ActionReference()).putProperty(sTT('channel'), sTT('selection')); (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1)

     eval("(ref2 = new ActionReference()).put" + (!v2 ? "Enumerated" : ((wP = v2 == 'workPath') ? "Property" : (isNaN(v2) ? "Name" : "Index")))

     + "(sTT('path'), " + (!v2 ? "sTT('ordinal'), sTT('targetEnum')" : (wP ? "sTT(v2)" : "v2")) + ")")

     dsc1.putReference(sTT('to'), ref2), dsc1.putInteger(sTT('version'), 1), dsc1.putBoolean(sTT('vectorMaskParams'), true)

     try{executeAction(sTT(v1), dsc1, DialogModes.NO)} catch(errPth) {}

     // v1: 'set', 'addTo', 'subtractFrom', 'interfaceWhite'; v2: null, 'workPath', path name

}

// LAYER FROM LAYER:

function cTL() {executeAction(sTT('copyToLayer'), undefined, DialogModes.NO)}

function pD(v) {// PATH DELETION:

     eval("(ref1 = new ActionReference()).put" + (!v ? "Enumerated" : ((wP = v == 'workPath') ? "Property" : (isNaN(v) ? "Name" : "Index")))

     + "(sTT('path'), " + (!v ? "sTT('ordinal'), sTT('targetEnum')" : (wP ? "sTT(v)" : "v")) + ")");

     (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1), executeAction(sTT('delete'), dsc1, DialogModes.NO)

     // v: null, 'workPath', path name / index

}

function rst() {// COLORS RESET

     (ref1 = new ActionReference()).putProperty(sTT('color'), sTT('colors'));

     (dsc1 = new ActionDescriptor()).putReference(sTT('null'), ref1)

     executeAction(sTT('reset'), dsc1, DialogModes.NO);

}

function str(v1, v2, v3, V1, V2, V3, V4) {// THREE STATES OF STROKE:

     for(i = 0; i < (arr = ['INSIDE', 'CENTER', 'OUTSIDE']).length; i++) {

          eval('sel.stroke(' + V1 + ',' + V2 + ', StrokeLocation.' + arr +

          ', ColorBlendMode.' + V3 + ',  ' + arguments + ', '  + V4 + ')' )

     }

}

cR2('shadows', false, false, 27), pat(2.7), SfP('set'), pD(), (sel = (aD = activeDocument).selection).grow(23, true), sel.invert()

sel.grow(23, true), sel.invert(), cTL(), aD.activeLayer = (lyr = aD.layers)[1], sel.selectAll(), rst(), sel.fill(backgroundColor)

sel.deselect(), aD.activeLayer = lyr[0],  str(25, 15, 5, 'foregroundColor', 1, 'MULTIPLY', false), aD.flatten()

dank2882
dank2882Author
Participating Frequently
February 1, 2018

Wow thank you, super creative and works very good. I'm going to run it against a large group and see what happens but thank you again.

Dan

Kukurykus
Legend
February 1, 2018

Let me know if that works, and upload other samples to test it on, perhaps those you have problem with. If you would like to automate thousends of your images (that you won't open one after one) let me know so I put that script into other grand script automating process. You'll have for example put all of them in some folder on desktop. Then by script select it to get started...