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

CS5.5 (MAC) script to fix random dots in lineart scans

Community Expert ,
Jan 06, 2013 Jan 06, 2013

Hello there.

In my line of work, I often have to scan text only documents to  print offset. The majority of the scans are clean but ultimately there will be random dots which need to be tidied up. I am looking for a way to streamline this process.

I was able to find a solution which I had in mind but it requires converting the lineart scan to 8-bit first. The example can be found at: http://verlagmartinkoch.at/software/cleanup/index.html

However, has anyone heard of an action or javascript that could do this without having to convert to greyscale and then back to lineart?

I have read DOZENS of DIY guides and posts suggesting always doing black and white scans as greyscale first and then converting them, but without going into tremendous detail, I will simply say that the scans I receive will output as lineart.

Many thanks

colly

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
TOPICS
Actions and scripting
6.6K
Translate
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 1 Correct answer

Contributor , Jan 14, 2013 Jan 14, 2013

I have created plugin for this

feel free to use it:

https://dl.dropbox.com/u/167681/ReprintMaster.plugin.zip

unfortunately filters don't work in lineart mode, so you'll have to convert image to Grayscale first. but my plugin is free unlike the one you mention in startpost, and works on mac - the one mentioned is windows only )

it shouldn't be a problem, just record action which converts to GS, calls my plugin and then converts back to lineart

If you need to work upon RGB/CMYK - you'll have to proces

...
Translate
Adobe
Community Expert ,
Jan 07, 2013 Jan 07, 2013

As Photoshop does not allow Filters and Quick Mask for 1bit bitmap images I see no reasonable way of avoiding going through grayscale and later on converting back to bitmap again. (Using Paths might be an option but I guess that would likely be more detrimental and slower.)

But as the conversions can be automated I don’t get why you even want to avoid them.

Translate
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
Guru ,
Jan 07, 2013 Jan 07, 2013

I can't see a problem with mode conversion… here is a script that did something along these lines a while back…

http://forums.adobe.com/message/2558998#2558998

Translate
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 ,
Jan 07, 2013 Jan 07, 2013

Thank you Muppet Mark. I did read that thread which you had linked from your post, but I'm not sure if it's quite the same thing that I'm looking for. In that thread, the OP had a desired size and wanted to remove stray pixels which were attached to a larger item. I'm not really trying to do that, but just remove stray black dots in a sea of white; or stray white dots in a sea of black... much like the example which i'd linked to in the opening post. The primary purpose of such a script would be used to clean up lineart scans of text only.

Put simply, the hard-copy scans are from a document feeder and scanned into a PDF. At the moment, I have been using the "edit object" feature within Acrobat to launch Photoshop and then do the necessary touchups, but am aware that it is possible to export all the scans to a folder of tiffs so that images can be changed en masse. The scans are lineart mainly to keep file sizes down and the fact that the scans are of text only and don't really need the general level of attention that cartoons or illustrations would. I suppose there is a general reluctance to convert to greyscale as it is an added step, and over what could be 400+ images, any timesavings are important.

I suppose the way that I believed the script may work would be to detect any black (or white) object < a certain amount of pixels in area... and invert the selection accordingly... but the reality is that I have no idea how that would work.

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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 ,
Jan 07, 2013 Jan 07, 2013

The scans are lineart mainly to keep file sizes down and the fact that the scans are of text only and don't really need the general level of attention that cartoons or illustrations would.

That should not be the only reason when working for print (and if the resolution of the bitmaps is high enough); grayscale images would get rasterized whereas 1bit bitmap images can utilize the full resolution of the ooutput device.

Translate
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
Guru ,
Jan 07, 2013 Jan 07, 2013

Did you try the script…? The filters used will clean out single pixel black dots in white invert and do the same with white dots in black… You didn't post a sample of your scans but can't you OCR them… That would be best

Translate
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 ,
Jan 07, 2013 Jan 07, 2013

@ c.pfaffenbichler

I think what was quoted was taken out of context. In my quest for this solution, many of the articles online point to scanning in greyscale first, tidying up and then ultimately converting to bitmap. The quote taken about lineart scans is meant as the scans are done as lineart FIRST as opposed to greyscale first and then all ultimately saved down to 1-bit bitmaps.

@ muppet mark

To take a line out of the other thread, I SHOULD go to specsavers . I continued reading past the first 15 posts (where i'd stopped before) and saw it through to the end. Yes, i tried the script and found an unforeseen fault (won't open tiffs with LZW compression) but otherwise seems to do what I want. Have to play with the size settings as I think 4pix is good for fine white spots but turns out the bits and pieces are a little bigger than that... but otherwise yes this appears to be the solution. cheers for that!

I can't post a sample of the art as I don't have the client's permission, and we're looking to reproduce the books "as is" rather than reset them via OCR. Luckily the books only have imprint changes and the content remains the same... thankfully! Have tried with a sample letter here and all appears to "do what it says on the jar"

colly

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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 ,
Jan 07, 2013 Jan 07, 2013

OK, have now had a chance to do some before and after comparisons and sadly, while the script is good, it is as I said in post 3 which is "not quite the same thing that I'm looking for".

With the before and after I've noticed that thin type is being "destroyed"

Screen shot 2013-01-08 at 12.26.50 AM.png

whereas other type and the little dots are being fixed nicely

Screen shot 2013-01-08 at 12.27.24 AM.png

but again, that's not quite what I was hoping the outcome would be. Again, I imagined that the script would look for any instance of black pixels with an area less than figure X and then invert them... It's hard for me to articulate so let me use this example:

Screen shot 2013-01-08 at 12.31.06 AM.png

in the image above, I imagine the script would work in a manner of "find all black pixels less than 9px" and that would remove the three dots on the sample (two of the dots are 3px in area, one is 4px) but leave everything else as it is without cleaning the edges.

I hope I have explained myself a little better.

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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 ,
Jan 07, 2013 Jan 07, 2013

Once again I think working with Paths is a viable option.

It’s (to me at least) not possible in Photoshop to determine the area circumscribed by a Path without Selections, but creating a Selection for each SubPathItem would likely make a Script too slow for practical purposes.

But if a check for a maximum width and height would suffice that might work out.

Translate
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 ,
Jan 07, 2013 Jan 07, 2013

Could you post a sample page (best with the antiqua text that proved problematic)?

Translate
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 ,
Jan 11, 2013 Jan 11, 2013

Like I say, I can't post a sample of the art as I don't have the client's permission but any lineart scan from a scanner would do. The result I am trying to achieve is effectively the same as the third image in post 7 with the exception of the three rogue dots that appear on the sample.

I have since tried to make selections of <9 square pixels by using the selection and filter tools and can now see the difficulty if a javascript is going to call on selection and filter tools to try and create the kind of selection that I want...  but would there be other ways to achieve the end result, perhaps at a binary level?

colly

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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 ,
Jan 12, 2013 Jan 12, 2013

Like I say, I can't post a sample of the art as I don't have the client's permission but any lineart scan from a scanner would do.

So we can create our own test files.

Will that help make results easily or reliably transferable to your files?

Anyway … a Scripting approach utilizing paths (barbaric, I guess, and not fast, about 22sec for a 2000px x 2000px image) can get this

birmapDirtScr2_2.jpg

from this

birmapDirtScr2_1.jpg

But if the letters (or their serifs) were only 1px wide and individual pixels only connected diagonally wholes might still result, as fas as I can tell.

Furthermore this also switches the image to grayscale and back intermediately.

Translate
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 ,
Jan 12, 2013 Jan 12, 2013

@ c.pfaffenbichler

good point, i suppose anything with type could have been supplied by me as a sample... put simply i couldn't upload bitmap tiffs to my wordpress blog for people to download. can however upload a PDF of the bitmap tiff, which is what i've done: http://wp.me/a1x3Yg-8k - it's really awful. on that sample I don't expect the jaggedness on the edges to be fixed, just the loose dots to go.

nevertheless i think your approach is the way to go. In your screengrabs it appears that the spicks and specks were 1px... what if they were larger, such as between 1 px and 9px across (81px square)? would the solution still work?

I think your demonstration is a good proof of principle. the time... well spotting the scans manually takes 5-6 minutes on average to do a decent job of them... as for the diagonals 1px... too bad.

but yes i think you are well on the right track  

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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
Contributor ,
Jan 12, 2013 Jan 12, 2013

Assuming you're working on a thresholded image in 8 bit, you can record a simple action. Make sure your eyedropper is set to a single pixel Sample Size.

  1. Select-> color range.  click on a black pixel, and click OK
  2. Select-> Modify -> contract.  Set to 1 to get rid of spots of with no area above 2X2, or set to 2 to get rid of spots without an area bigger than 4x4.  OK
  3. Select-> Grow
  4. Select -> Inverse
  5. Edit -> Fill.    Set to Use: White. OK.
  6. Repeat steps 2 - 4
  7. Edit -> Fill.    Set to Use: Black. OK.
Translate
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 ,
Jan 14, 2013 Jan 14, 2013

Sounds like a good idea.

Translate
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 ,
Jan 14, 2013 Jan 14, 2013

@David

It's a great solution which gets the results, but again has issues.

Because of the way the modify/contract method works, it does not take long before any increase in selection values is not only removing dots, but lines and underlines as well. A selection of 1, it will remove spots 2x2 (4px) as stated... with a selection of 2 it will remove spots 4x4, (16px) with a selection of 3, it will remove any graphics with a width or height less than or equal to 6x6. (36px)

This solution will work well for type but not so great for thin lines such as text which may be underlined, or thin lines in general. For example, an underline of a word may only be 6px high but would be hundreds of pixels across. If a script was to search by min area (e.g. min 81 px) the line would survive, but if the action is used with a contract threshold of 3, then the line will ultimately be removed despite the fact that it is more than 81px in total area.

However, David's suggestion is the closest solution to the problem presented at the moment.

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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 ,
Jan 14, 2013 Jan 14, 2013

Here’s a screenshot of part of the image before and after.

birmapDirtScr3.jpg

You could give the Script a try if you want.

The line

if (theWidth < 7 && theHeight < 7) {theNewArray.push(theSubArray)};

sets the with and height under which a segment is supposed to get removed and the pretty much arbitrary number 19 in line

var theArray = collectPathInfoFromDesc2012belowXPoints(theCopy, thePath, 19);

is intended to exclude SubPathItems with a lot of PathPoints from evaluation right away to speed things up.

// cover up anything under 7x7px;

// 2012,  use it at your own risk;

#target photoshop

if (app.documents.length > 0 && app.activeDocument.mode = DocumentMode.BITMAP) {

var myDocument = app.activeDocument;

var originalRulerUnits = app.preferences.rulerUnits;

app.preferences.rulerUnits = Units.PIXELS;

var theResolution = myDocument.resolution;

myDocument.resizeImage(null, null, 72, ResampleMethod.NONE);

// make copy and scale up to ascertain making pathpoints for pixel corners;

var theCopy = myDocument.duplicate ("thecopy", true);

theCopy.resizeImage(null, null, 144, ResampleMethod.NEARESTNEIGHBOR);

// make seletion from rgb-channel;

// =======================================================

var idsetd = charIDToTypeID( "setd" );

    var desc3 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref1 = new ActionReference();

        var idChnl = charIDToTypeID( "Chnl" );

        var idfsel = charIDToTypeID( "fsel" );

        ref1.putProperty( idChnl, idfsel );

    desc3.putReference( idnull, ref1 );

    var idT = charIDToTypeID( "T   " );

        var ref2 = new ActionReference();

        var idChnl = charIDToTypeID( "Chnl" );

        var idChnl = charIDToTypeID( "Chnl" );

        var idRGB = charIDToTypeID( "RGB " );

        ref2.putEnumerated( idChnl, idChnl, idRGB );

    desc3.putReference( idT, ref2 );

executeAction( idsetd, desc3, DialogModes.NO );

// =======================================================

var idInvs = charIDToTypeID( "Invs" );

executeAction( idInvs, undefined, DialogModes.NO );

// create path;

theCopy.selection.makeWorkPath(0.5);

var thePath = theCopy.pathItems[theCopy.pathItems.length - 1];

// scale back down;

theCopy.resizeImage(null, null, 72, ResampleMethod.NEARESTNEIGHBOR);

// get path info for subpathitems with less than x pathpoints;

var theArray = collectPathInfoFromDesc2012belowXPoints(theCopy, thePath, 19);

// close copy;

theCopy.close(SaveOptions.DONOTSAVECHANGES);

app.activeDocument = myDocument;

////////////////////////////////////

// get just the pathpoints;

var theNewArray = new Array;

// work through the original array;

for (var m = 0; m < theArray.length; m++) {

//$.writeln (m);

          var theSubArray = theArray;

                    var checkHorArray = new Array;

                    var checkVerArray = new Array;

                    for (var n = 0; n < theSubArray.length - 2; n++) {

                              checkHorArray.push(theSubArray[0][0]);

                              checkVerArray.push(theSubArray[0][1]);

                              };

                    checkHorArray.sort();

                    checkVerArray.sort();

                    var theWidth = checkHorArray[checkHorArray.length -1] - checkHorArray[0];

                    var theHeight = checkVerArray[checkVerArray.length -1] - checkVerArray[0];

// use subpathitem only if width and height below x px;

                    if (theWidth < 7 && theHeight < 7) {theNewArray.push(theSubArray)};

          };

//create path;

var thisDate = new Date();

var thisPath = createPath2012(theNewArray, "heights_"+thisDate.getTime());

// =======================================================

var idCnvM = charIDToTypeID( "CnvM" );

    var desc2 = new ActionDescriptor();

    var idT = charIDToTypeID( "T   " );

        var desc3 = new ActionDescriptor();

        var idRt = charIDToTypeID( "Rt  " );

        desc3.putInteger( idRt, 1 );

    var idGrys = charIDToTypeID( "Grys" );

    desc2.putObject( idT, idGrys, desc3 );

executeAction( idCnvM, desc2, DialogModes.NO );

//

myDocument.pathItems[myDocument.pathItems.length - 1].makeSelection(0, false);

myDocument.layers[0].invert();

myDocument.selection.deselect();

// =======================================================

var idCnvM = charIDToTypeID( "CnvM" );

    var desc4 = new ActionDescriptor();

    var idT = charIDToTypeID( "T   " );

        var desc5 = new ActionDescriptor();

        var idRslt = charIDToTypeID( "Rslt" );

        var idRsl = charIDToTypeID( "#Rsl" );

        desc5.putUnitDouble( idRslt, idRsl, myDocument.resolution );

        var idMthd = charIDToTypeID( "Mthd" );

        var idMthd = charIDToTypeID( "Mthd" );

        var idThrh = charIDToTypeID( "Thrh" );

        desc5.putEnumerated( idMthd, idMthd, idThrh );

    var idBtmM = charIDToTypeID( "BtmM" );

    desc4.putObject( idT, idBtmM, desc5 );

executeAction( idCnvM, desc4, DialogModes.NO );

// fill path;

//fillPathWhite();

// reset;

app.preferences.rulerUnits = originalRulerUnits;

};

////////////////////////////////////

////////////////////////////////////

////////////////////////////////////

////// collect path info from actiondescriptor, smooth added //////

function collectPathInfoFromDesc2012belowXPoints (myDocument, thePath, theNumber) {

var originalRulerUnits = app.preferences.rulerUnits;

app.preferences.rulerUnits = Units.POINTS;

// based of functions from xbytor’s stdlib;

var ref = new ActionReference();

for (var l = 0; l < myDocument.pathItems.length; l++) {

          var thisPath = myDocument.pathItems;

          if (thisPath == thePath && thisPath.name == "Work Path") {

                    ref.putProperty(cTID("Path"), cTID("WrPt"));

                    };

          if (thisPath == thePath && thisPath.name != "Work Path" && thisPath.kind != PathKind.VECTORMASK) {

                    ref.putIndex(cTID("Path"), l + 1);

                    };

          if (thisPath == thePath && thisPath.kind == PathKind.VECTORMASK) {

        var idPath = charIDToTypeID( "Path" );

        var idPath = charIDToTypeID( "Path" );

        var idvectorMask = stringIDToTypeID( "vectorMask" );

        ref.putEnumerated( idPath, idPath, idvectorMask );

                    };

          };

var desc = app.executeActionGet(ref);

var pname = desc.getString(cTID('PthN'));

// create new array;

var theArray = new Array;

var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));

// for subpathitems;

for (var m = 0; m < pathComponents.count; m++) {

          var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));

          var operation1 = pathComponents.getObjectValue(m).getEnumerationValue(sTID("shapeOperation"));

          switch (operation1) {

                    case 1097098272:

                    var operation = 1097098272 //cTID('Add ');

                    break;

                    case 1398961266:

                    var operation = 1398961266 //cTID('Sbtr');

                    break;

                    case 1231975538:

                    var operation = 1231975538 //cTID('Intr');

                    break;

                    default:

//                    case 1102:

                    var operation = sTID('xor') //ShapeOperation.SHAPEXOR;

                    break;

                    };

// for subpathitem’s count;

          for (var n = 0; n < listKey.count; n++) {

                    var points = listKey.getObjectValue(n).getList(sTID('points'));

if (points.count < theNumber) {

                    theArray.push(new Array);

                    try {var closed = listKey.getObjectValue(n).getBoolean(sTID("closedSubpath"))}

                    catch (e) {var closed = false};

// for subpathitem’s segment’s number of points;

                    for (var o = 0; o < points.count; o++) {

                              var anchorObj = points.getObjectValue(o).getObjectValue(sTID("anchor"));

                              var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];

                              var thisPoint = [anchor];

                              try {

                                        var left = points.getObjectValue(o).getObjectValue(cTID("Fwd "));

                                        var leftDirection = [left.getUnitDoubleValue(sTID('horizontal')), left.getUnitDoubleValue(sTID('vertical'))];

                                        thisPoint.push(leftDirection)

                                        }

                              catch (e) {

                                        thisPoint.push(anchor)

                                        };

                              try {

                                        var right = points.getObjectValue(o).getObjectValue(cTID("Bwd "));

                                        var rightDirection = [right.getUnitDoubleValue(sTID('horizontal')), right.getUnitDoubleValue(sTID('vertical'))];

                                        thisPoint.push(rightDirection)

                                        }

                              catch (e) {

                                        thisPoint.push(anchor)

                                        };

                              try {

                                        var smoothOr = points.getObjectValue(o).getBoolean(cTID("Smoo"));

                                        thisPoint.push(smoothOr)

                                        }

                              catch (e) {thisPoint.push(false)};

                              theArray[theArray.length - 1].push(thisPoint);

                              };

                    theArray[theArray.length - 1].push(closed);

                    theArray[theArray.length - 1].push(operation);

                    };

};

          };

// by xbytor, thanks to him;

function cTID (s) { return cTID || cTID = app.charIDToTypeID(s); };

function sTID (s) { return sTID || sTID = app.stringIDToTypeID(s); };

// reset;

app.preferences.rulerUnits = originalRulerUnits;

return theArray;

};

////// create a path from collectPathInfoFromDesc2012-array //////

function createPath2012(theArray, thePathsName) {

var originalRulerUnits = app.preferences.rulerUnits;

app.preferences.rulerUnits = Units.POINTS;

// thanks to xbytor;

cTID = function(s) { return app.charIDToTypeID(s); };

sTID = function(s) { return app.stringIDToTypeID(s); };

    var desc1 = new ActionDescriptor();

    var ref1 = new ActionReference();

    ref1.putProperty(cTID('Path'), cTID('WrPt'));

    desc1.putReference(sTID('null'), ref1);

    var list1 = new ActionList();

for (var m = 0; m < theArray.length; m++) {

          var thisSubPath = theArray;

    var desc2 = new ActionDescriptor();

    desc2.putEnumerated(sTID('shapeOperation'), sTID('shapeOperation'), thisSubPath[thisSubPath.length - 1]);

    var list2 = new ActionList();

    var desc3 = new ActionDescriptor();

    desc3.putBoolean(cTID('Clsp'), thisSubPath[thisSubPath.length - 2]);

    var list3 = new ActionList();

for (var n = 0; n < thisSubPath.length - 2; n++) {

          var thisPoint = thisSubPath;

    var desc4 = new ActionDescriptor();

    var desc5 = new ActionDescriptor();

    desc5.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[0][0]);

    desc5.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[0][1]);

    desc4.putObject(cTID('Anch'), cTID('Pnt '), desc5);

    var desc6 = new ActionDescriptor();

    desc6.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[1][0]);

    desc6.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[1][1]);

    desc4.putObject(cTID('Fwd '), cTID('Pnt '), desc6);

    var desc7 = new ActionDescriptor();

    desc7.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[2][0]);

    desc7.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[2][1]);

    desc4.putObject(cTID('Bwd '), cTID('Pnt '), desc7);

    desc4.putBoolean(cTID('Smoo'), thisPoint[3]);

    list3.putObject(cTID('Pthp'), desc4);

          };

    desc3.putList(cTID('Pts '), list3);

    list2.putObject(cTID('Sbpl'), desc3);

    desc2.putList(cTID('SbpL'), list2);

    list1.putObject(cTID('PaCm'), desc2);

          };

    desc1.putList(cTID('T   '), list1);

    executeAction(cTID('setd'), desc1, DialogModes.NO);

if (hasVectorMask() == false) {

var myPathItem = app.activeDocument.pathItems[app.activeDocument.pathItems.length - 1];

}

else {

var myPathItem = app.activeDocument.pathItems[app.activeDocument.pathItems.length - 2];

};

myPathItem.name = thePathsName;

app.preferences.rulerUnits = originalRulerUnits;

return myPathItem

};

// from »Flatten All Masks.jsx« by jeffrey tranberry;

///////////////////////////////////////////////////////////////////////////////

// Function: hasVectorMask

// Usage: see if there is a vector layer mask

// Input: <none> Must have an open document

// Return: true if there is a vector mask

///////////////////////////////////////////////////////////////////////////////

function hasVectorMask() {

          var hasVectorMask = false;

          try {

                    var ref = new ActionReference();

                    var keyVectorMaskEnabled = app.stringIDToTypeID( 'vectorMask' );

                    var keyKind = app.charIDToTypeID( 'Knd ' );

                    ref.putEnumerated( app.charIDToTypeID( 'Path' ), app.charIDToTypeID( 'Ordn' ), keyVectorMaskEnabled );

                    var desc = executeActionGet( ref );

                    if ( desc.hasKey( keyKind ) ) {

                              var kindValue = desc.getEnumerationValue( keyKind );

                              if (kindValue == keyVectorMaskEnabled) {

                                        hasVectorMask = true;

                              }

                    }

          }catch(e) {

                    hasVectorMask = false;

          }

          return hasVectorMask;

};

function fillPathWhite () {

// =======================================================

var idRset = charIDToTypeID( "Rset" );

    var desc8 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref6 = new ActionReference();

        var idClr = charIDToTypeID( "Clr " );

        var idClrs = charIDToTypeID( "Clrs" );

        ref6.putProperty( idClr, idClrs );

    desc8.putReference( idnull, ref6 );

executeAction( idRset, desc8, DialogModes.NO );

// =======================================================

var idExch = charIDToTypeID( "Exch" );

    var desc9 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref7 = new ActionReference();

        var idClr = charIDToTypeID( "Clr " );

        var idClrs = charIDToTypeID( "Clrs" );

        ref7.putProperty( idClr, idClrs );

    desc9.putReference( idnull, ref7 );

executeAction( idExch, desc9, DialogModes.NO );

// =======================================================

var idFl = charIDToTypeID( "Fl  " );

    var desc10 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref8 = new ActionReference();

        var idPath = charIDToTypeID( "Path" );

        var idOrdn = charIDToTypeID( "Ordn" );

        var idTrgt = charIDToTypeID( "Trgt" );

        ref8.putEnumerated( idPath, idOrdn, idTrgt );

    desc10.putReference( idnull, ref8 );

    var idWhPt = charIDToTypeID( "WhPt" );

    desc10.putBoolean( idWhPt, true );

executeAction( idFl, desc10, DialogModes.NO );

};

//

Translate
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
Contributor ,
Jan 14, 2013 Jan 14, 2013

I have created plugin for this

feel free to use it:

https://dl.dropbox.com/u/167681/ReprintMaster.plugin.zip

unfortunately filters don't work in lineart mode, so you'll have to convert image to Grayscale first. but my plugin is free unlike the one you mention in startpost, and works on mac - the one mentioned is windows only )

it shouldn't be a problem, just record action which converts to GS, calls my plugin and then converts back to lineart

If you need to work upon RGB/CMYK - you'll have to process each channel separately, I didn't code it yet.

Rough white/rough balck option will not account for diagonal pixel connections

before - after

https://dl.dropbox.com/u/167681/scan0329.png

here's a quick demo

https://dl.dropbox.com/u/167681/reprint_master_1.gif

https://dl.dropbox.com/u/167681/Screen-shot-2.gif

(plugin is 32bit only, going to update for CS6 soon)

Translate
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 ,
Jan 15, 2013 Jan 15, 2013

@Evgeny_Trefilov

I think we have the answer! still have to do more testing but on first inspection it appears that this is correct.

The slider "black size" i've worked out, but "white size" I'm not sure.

to get this to work on text with pinholes in it, the procedure appears to be run reprintmaster (with a threshold of 128, black of 70, white of 1), then invert, run reprintmaster with the same settings, then invert again.

Would be great to see the script once it is ready for 64 bit too, please keep us posted.

@c.pfaffenbichler

I did try the javascript but found that, as you say, isn't fast, but doesn't remove a lot of the stray dots either. For example, stray dots which measure 8px wide by 2px high remain in the art because the script will only remove items under 7px AND 7px... (49 square pixels). This means that anything which has an area under 49px BUT has a width or height over 7px will be missed by the script.

Thank you everyone for all the efforts for determining a solution for this issue.

colly

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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 ,
Jan 15, 2013 Jan 15, 2013

You could edit the line that sets the 7px limit, but a proper plug-in is a better approach anyway and probably much faster.

Translate
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
Contributor ,
Jan 15, 2013 Jan 15, 2013

>The slider "black size" i've worked out, but "white size" I'm not sure.

>to get this to work on text with pinholes in it, the procedure appears to be run reprintmaster (with a threshold of 128, black of 70, white of 1), then invert, run reprintmaster with >the same settings, then invert again.

no need to run in 2 phases with invert - white size does exactly that. i.e. set white to 10, and it will fill white holes with size <=10 with black.

Translate
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 ,
Jan 15, 2013 Jan 15, 2013

no need to run in 2 phases with invert - white size does exactly that. i.e. set white to 10, and it will fill white holes with size <=10 with black.

well... it's not working in my case. to demonstrate, here is a sample of some white pinholes in the E to eliminate, along with black pinholes in the white.

Screen shot 2013-01-15 at 11.50.40 PM.png

and here is the result

Screen shot 2013-01-15 at 11.50.51 PM.png

I have also tried this with 10 and to no avail. Have I missed something?

Running Photoshop CS5.1 on Mac in 32bit mode

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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
Contributor ,
Jan 15, 2013 Jan 15, 2013

hm, could be some experimental build - I've started to improve defects on edges, but cancelled somewhere.

I have tried version I have installed on my production mac, and it works fine - here it goes

https://dl.dropbox.com/u/167681/ReprintMaster_production.plugin.zip

(original link is corrected as well)

Translate
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
Contributor ,
Dec 31, 2013 Dec 31, 2013

Here's 64bit version, as promised

Plugin is pretty different now, I've added lots of thresholding methods (it's using OpenCV now)

Preview is available if you run it from Automate->Threshold

It can be run from Filter->Evgeny Trefilov->Threshold as well, but there's no preview in this case, just parameters dialog

https://dl.dropboxusercontent.com/u/167681/Threshold.plugin.zip

Translate
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 ,
Dec 31, 2013 Dec 31, 2013

Thank you very much for that. It would certain benefit us if the how the sliders work could be explained.

But thank you for providing a 64 bit version!

Colin

If the answer wasn't in my post, perhaps it might be on my blog at colecandoo!
Translate
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