• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
1

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

Explorer ,
Jan 30, 2018 Jan 30, 2018

Copy link to clipboard

Copied

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.

Screen Shot 2018-01-30 at 11.45.48 AM.png

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

TOPICS
Actions and scripting

Views

5.1K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 2 Correct answers

LEGEND , Jan 31, 2018 Jan 31, 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 Action

...

Votes

Translate

Translate
LEGEND , Feb 07, 2018 Feb 07, 2018

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

...

Votes

Translate

Translate
Adobe
LEGEND ,
Jan 31, 2018 Jan 31, 2018

Copy link to clipboard

Copied

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()

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Feb 01, 2018 Feb 01, 2018

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 01, 2018 Feb 01, 2018

Copy link to clipboard

Copied

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...

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Feb 01, 2018 Feb 01, 2018

Copy link to clipboard

Copied

I am gathering some test pages now and I also have a question but I am getting an example ready.

I would love to have an automated script and a folder on the desktop would be prefect. I'll be back in around 20 mins.

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Feb 01, 2018 Feb 01, 2018

Copy link to clipboard

Copied

Ok here is a link to one of the files we actually work with Dropbox - 407_L.psd

So notice that the black text (layer 1) is actually separated from the white background (Color Fill 1)  and we leave an original in the file but its hidden (Layer 0). We use 300 dpi on the files and this link is to one of the worst files we have.

Also I was wondering with the path setup you are doing is there an easy way to fill the letters that have white or holes in them?

Screen Shot 2018-02-01 at 10.49.07 AM.png

Please let us know what we can do for you,

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 01, 2018 Feb 01, 2018

Copy link to clipboard

Copied

Wow, great work of me. I didn't suppose that will work also on other document (you just uploaded) that is not only one layer-background. It reduced over 80 percent of black dots. And when I flattened image before processing the result was much beter - over 90 percent reducement without affecting letters, though that sample was more advanced than 1st one.

Actually I wrote that part of filling white holes in letter too. But didn't attach to script as wasn't sure you need it. It makes letters are more complete, however some where little white spaces are part of letters may be blacked too, like in that 'M'.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Feb 01, 2018 Feb 01, 2018

Copy link to clipboard

Copied

Is there a way in the code to make the selection of the dots a little larger?

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 02, 2018 Feb 02, 2018

Copy link to clipboard

Copied

I'll check that later if I get what you mean, but probably bit increment of pat function value that is now set to 2.7 could help.

UPDATE:

Add this function under others they are in code:

function BaC(v1, v2, v3) {// BRIGTHNESS AND CONTRAST:

     (dsc1 = new ActionDescriptor()).putInteger(sTT('brightness'), v1)

     dsc1.putInteger(sTT('center'), v2), dsc1.putBoolean(sTT('useLegacy'), !v3 ? false : v3)

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

}

Add this code at the bottom of script:

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()

This additionall part is going to remove 100% little black dots and make partial reconstruction of letters. Small grey areas in letters will be turned into black while white holes will be decreased. A little changed letters (looking at with zoom) are now less frayed, however to keep their original shape (in about 95%) there couldn't be possible complete white dots elimination.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Feb 05, 2018 Feb 05, 2018

Copy link to clipboard

Copied

Thank you so much, we are going to run it against a bunch of files today. Looking forward to it.

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 05, 2018 Feb 05, 2018

Copy link to clipboard

Copied

Let me know all works as you want so I will find time to automate this process for many files (but it may take over an hour)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Feb 05, 2018 Feb 05, 2018

Copy link to clipboard

Copied

We are getting and error when we try to run the script.

Screen Shot 2018-02-05 at 3.32.32 PM.jpeg

I'm afraid i put the new code in wrong.

Can you send me the code in the order you have it?

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 05, 2018 Feb 05, 2018

Copy link to clipboard

Copied

Last 2 paragraphs of old code replace to these 4:

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 + ')' )

     }

}

function BaC(v1, v2, v3) {// BRIGTHNESS AND CONTRAST:

     (dsc1 = new ActionDescriptor()).putInteger(sTT('brightness'), v1)

     dsc1.putInteger(sTT('center'), v2), dsc1.putBoolean(sTT('useLegacy'), !v3 ? false : v3)

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

}

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()

If it still doesn't work though you have correct order then maybe it doesn't work on specific layer. I tested it only on that one you lately attached. So there's chance something could go wrong but I believe you didn't combined well old code with new.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Feb 06, 2018 Feb 06, 2018

Copy link to clipboard

Copied

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

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 07, 2018 Feb 07, 2018

Copy link to clipboard

Copied

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)

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Mar 03, 2023 Mar 03, 2023

Copy link to clipboard

Copied

Hello,

A few years back you helped create a script to remove dots from these old pages we are working with. I have been using the script off and on for years now but now that I have Adobe Photoshop 2023 I am getting an error. Please see attached image for error. 

I was hoping you would be willing to update the script so I can keep using it. I have also attached a file I am using for you to test on.

 

Thank you again!!

 

Dan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 03, 2023 Mar 03, 2023

Copy link to clipboard

Copied

I believe that you will need to contact Kukurykus at:

 

https://www.ps-scripts.com

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Mar 03, 2023 Mar 03, 2023

Copy link to clipboard

Copied

Ah thank you so much!! really appreciate the help.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Jun 17, 2023 Jun 17, 2023

Copy link to clipboard

Copied

LATEST

Hi, did you get this working in Photoshop 2023? I have a few documents that have alot of black dots and it is taking hours to clean them up. I would really appreciate a copy of a script like this to help me.

 

Thanks,

Paul

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines