''' InpaintingMethod3B.py This is method 3 B. It differs from 3 (A) in that it averages only "good" pixels during the relaxation. Basically it propagates out from the edges and does not average in values from any invalid pixels. An invalid pixel is either (a) a barrier pixel or (b) an as yet unpainted pixel. During each iteration, it updates a per-pixel variable (stored in the Green component of the combined image) that is the number of VALID neighbors. This equals 4 - #invalidNeighbors. -------- At each pixel within the inpainting region, compute the an average of the pixel values of its "valid" neighbors. Here we use the weights of 0.25 at each of N,E,W,S. Method 3 adds "barriers" within the inpainting region. A barrier pixel is indicated in the input image by the presence of a pure red pixel. To separate these, use the Mask together with the "damaged with barriers" image. Red pixels inside the inpainting regions are barrier pixels. The method allows barrier pixels to take values like other pixels do within the inpainting region. However, they are not allowed to contribute to the values of other pixels. Pixel may have any number between 0 and 3 of its neighbors as barrier pixels, because if it had 4 then it could not be inpainted at all. We need four separate rules for updating pixels, depending on their numbers of barrier pixels. One rule divides the sum of values by 4, another by 3, another by 2 and another by 1. Actually the same rule will work, provided we have a separate image containing the number of barrier pixels in the neighborhood. In order to use PixelMath with effectively 3 source images rather than 2, we can encode the Mask and the number of barrier pixels into the red and green components of one image. So the algorithm needs to compute the number of barrier pixels at each location as a first step. Open the following windows. damagedWithBarriers: Image to be fixed. mask: Mask image identifying the pixels to be inpainted. Barriers(x,y)=if DamagedWithBarriers(x,y)=RGB(255,0,0) then 1 else 0. counts(x,y) = S1(x-1,y)+S1(x,y-1)+S(x+1,y)+S1(x,y+1) CombinedMaskAndBarriers(x,y)=RGB(if S1(x,y)=255 then 1 else 0, NumBarriers(x,y), 0) NonBarrierPixels (current image, except 0s where barrier pixels are) current: Source for update iteration new: Destination for iterations. diff: difference image (absolute values) accum: image for accumulating sums of differences. ''' damagedWB = pmOpenImage(0, '../Inpainting/damagedWithBarriers3.png') mask = pmOpenImage(0, '../Inpainting/Mask2.png') w = pmGetImageWidth(mask) h = pmGetImageHeight(mask) ''' Clean up the input image within the inpainting areas. It is supposed to be pure black, but maybe during Writing by the GIMP, some pixels got set a little greater. They need to be exactly 0 because that's the cue used to determine whether they are still unpained.''' pmSetSource1(damagedWB) pmSetSource2(mask) pmSetDestination(damagedWB) # If within inpaint area and not barrier pixel then set to 0 pmSetFormula("If Red2(x,y)>10 then if Red1(x,y) < 10 then 0 else 255 else s1(x,y)") pmCompute() barriers= pmNewImage(0, "Barriers", w, h, 0, 0, 0) barcnt = pmNewImage(0, "Count of Barrier pixel neighbors (static)", w, h, 0, 0, 0) counts = pmNewImage(0, "Counts of valid pixels in neighborhood (dynamic)", w, h, 0, 0, 0) valids = pmNewImage(0, "1 if pixel can be used, 0 otherwise (dynamic)", w, h, 0, 0, 0) combined= pmNewImage(0, "Combined: mask, counts, barriers", w, h, 0, 0, 0) current = pmNewImage(0, "Current version", w, h, 0, 0, 0) updated = pmNewImage(0, "updated version", w, h, 0, 0, 0) diff = pmNewImage(0, "Difference of last two", w, h, 0, 0, 0) temp = pmNewImage(0, "Half-combined (temporary)", w, h, 0, 0, 0) accum = pmNewImage(0, "Accumulator (temporary)", w, h, 0, 0, 0) noBarr = pmNewImage(0, "Current but 0 at barriers", w, h, 0, 0, 0) pmPositionWindow(updated, 700, 300, 2*w, 2*h) pmZoom(updated, w/2, h/2) pmHideProgress() # Isolate the barrier pixels pmSetSource1(damagedWB) pmSetSource2(mask) pmSetDestination(barriers) pmSetFormula("If Red1(x,y)>250 and Red2(x,y)>1 then 1 else 0") pmCompute() # Find the counts of barrier in each neighborhood. pmSetSource1(barriers) pmSetSource2(current) pmSetDestination(barcnt) pmSetFormula("if x>0 then S1(x-1,y) else 0)+(if y>0 then S1(x,y-1) else 0)+(if x0 and S2(x,y)=0 then 1 else 0") pmCompute() # Count the number of valid neighbors pmSetSource2(valids) pmSetDestination(counts) pmSetFormula("(if x>0 then S2(x-1,y) else 0)+(if y>0 then S2(x,y-1) else 0)+(if x1 then 1 else 0, S2(x,y), 0)") pmCompute() # Make the blue component hold the barrier characteristic fn. pmSetSource1(temp) pmSetSource2(barriers) pmSetDestination(combined) pmSetFormula("(S1(x,y)+RGB(0,0,S2(x,y)))") pmCompute() # perform relaxation: pmSetSource1(noBarr) pmSetSource2(combined) pmSetDestination(updated) # If pixel is in inpainting region, then average its valid neighbors. Else take orig. value. pmSetFormula("if Red2(x,y)>0 then if Green2(x,y)>0 then (1/(Green2(x,y)))*((if x>0 then S1(x-1,y) else 0)+(if y>0 then S1(x,y-1) else 0)+(if x