delete extra lines in image - algorithm

I have an image with nearly smooth background with some extra lines on it. I want to convert the image from RGB color space to LAB color space and then average the "L" part of pixels.
But before converting I want to delete extra lines or somehow ignore lines pixels in averaging the "L" part. Is there any algorithm to do this?
Below is an example of the images I have.

An option is to compute the gradient (Sobel for instance) and avoid doing the accumulation where the gradient magnitude is significant.
Following the comment by #Paul, it will be interesting to see the influence of the threshold level on the computed average.

I am using ImageMagick like your other question.
This will give you a mask of all the pixels that differ by more than 5% from their surrounding area (it's the 0.05 in the -fx part). It works by loading the stone image, copying it and blurring the copy to remove local details. It then calculates the absolute difference between the original and the blurred copy and sets the pixel to 1 if the difference exceeds 5% else sets it to 0:
convert stone.jpg -colorspace gray \( +clone -blur x10 \) -fx "abs(u-v)>0.05?1:0" mask.png
Experiment with changing the 0.05 and see what you think.
This will tell you how many pixels in the mask are white. It relies on the mean being sum of pixel brightnesses divided by number of pixels and knowing all pixels are either 0 or 1:
convert mask.png -format "%[fx:int(mean*w*h)]" info:
6155
This will blacken all the masked pixels. It works by loading both the stone and the mask, inverting the mask and then, at each pixel position, choosing the darker of the two images - so it will choose black everywhere the mask is white and it will choose the stone everywhere else:
convert stone.jpg \( mask.png -negate \) -compose darken -composite nolines.png

In ImageMagick, if you make the pixels to be ignored transparent, you can get the average of all non-transparent pixels using -scale 1x1!. For example, from the two images above:
So first put the mask into the alpha channel, then scale the result to one pixel, turn alpha off and get the pixel color:
convert image.jpg mask.png -alpha off -compose copy_opacity -composite -scale 1x1! -alpha off -format "%[pixel:u.p{0,0}]" info:
srgb(231,214,198)
To check, lets make a swatch:
convert -size 100x100 xc:"srgb(231,214,198)" swatch.png
Or we can recolor the original image:
convert image.jpg -fill "srgb(231,214,198)" -colorize 100 newimage.png

Related

is there a way to use imagemagick or another graphics library to filter and image based on the pixels in another image

So I wrote a program to do this, but it takes forever to execute. I've noticed a lot of graphics libraries seem to execute stuff way faster than the things I code. Basically what I want to do is filter the pixels in the first image using the second one. If they don't match replace them with black. I just want to be able to see the wall in the image.
In ImageMagick, you can extract the unique (srgb) colors from your image2 after limiting the colors to 255. Then you can loop over each color and have it fill that color with white in the first image and all other colors with black. This makes a mask image, which can be multiplied with image1 to make your result. Adjust the fuzz value as desired.
colors=`convert image2.jpg -fuzz 10% +dither -colors 255 -unique-colors txt: | cut -d\ -f6`
list=""
for color in $colors; do
val="-fill white -opaque '$color'"
list="$list $val"
done
eval 'convert image1.png -fuzz 1% '$list' -fill black +opaque white mask.png'
convert image1.png mask.png -compose multiply -composite result.png

How to convert coloured Captchas to Grey Scale?

I'm trying to make a capcha solver, but I have ran into some trouble. The captcha that I am trying to solve has different coloured backgrounds.
I need to convert it to black text on white background so that it could easily be recognised by tesseract-ocr
I have tried
convert *.png -threshold 50% *.png which only shows some of the digits.
The problem with simple 50% thresholding is that both colours may be lighter than 50% grey and will therefore come out as white. Or, conversely, both colours may be darker than mid-grey and therefore bith come out as black.
You need to do a 2-colour quantisation to get just 2 colours, then go to greyscale and normalize so the lighter colour goes white and the darker one goes black. I am not near a computer, to test, but that should be:
convert input.png -colors 2 -colorspace gray -normalize result.png
Now, you will find some images are inverted (black on white instead of white on black), so you can either test the top left corner pixel and if it is white, then invert the image. Or, you could get the mean of the image and if it is more than 0.5 that would indicate that the image is largely white and therefore needs inverting.
Invert with:
convert input.png -negate output.png
Get top-left pixel with:
convert image.png -format '%[pixel:p{0,0}]' info:-
Get mean value with:
convert image.png -format "%[mean]" info:-

How can I stretch the edge colors of elements in an image with alpha channel?

I have an PNG with an alpha channel. First I want to find all elements in the png, then I want to stretch the last color on the border about 5 to 10 pixels out
The stretching has to be in all directions from the center of each object on the image (like triangle, rectangle...)
It is important that I keep the color of each pixel on the edge
Is this possible? Goal is in the future to automate the process with ImageMagick.
I have had a play with distort and it is probably the way to go but needs more investigation. I was working on a smaller image and the code is basically a proof of concept.
I think edge is the key option and with this example I reduced the size of the resized image all round by 20px in the distort.
I do not know how you would get the angled edges though.I know you do not want to distort the original but it may give you some ideas as to how to get the result you want.
convert "1.png" -trim -matte -virtual-pixel edge -mattecolor none -interpolate Spline +distort BilinearForward "0,0 20,20 400,0 380,20 400,95 380,55 0,95 20,55" -trim +repage "result.png"
I am not quite sure what is going on at the edges of your image, but this should get you started...
First, I trim off any transparent edges (with -trim) so we get to the actual pattern you seek to extend. Then clone the image and dispose of everything except the top row of pixels (with -crop). Then scale that row up till it is 800 pixels tall. Then exchange the tall top row with the original image in the processing order (using +swap) and append the original image below the height-extended top row.
convert cells.png -trim +repage \( +clone -crop x1+0+0 -scale x800! \) +swap -append result.png

How to replace a color by another?

This is general question (between programming and math):
How to replace a color by another in a bitmap?
I assume the bitmap is a 2D-array
Example : Let's replace RGB color [234,211,23] by RGB color [234,205,10].
How to do this color replacement, such that the neighbour colors are replaced as well ? I.e. a smooth color replacement.
I assume there exists methods like linear interpolation for neighbour colors, etc.
What are the classical ways to do this?
Here is an example of how to detect color RGB 234,211,23 and its neighbour colors in a 500x500px image bitmap array x:
for i in range(500):
for j in range(500):
if abs(x[i,j][0] - 234) < TRESH and abs(x[i,j][1] - 211) < TRESH and abs(x[i,j][2] - 23) < TRESH:
x[i,j] = ... # how to set the new color in a smooth way?
I think that a good approach that you can use is to change the whole image to a new color space, I'd rather use HSV color space instead of RGB, you can find some info here: HSV color Space.
When you wish to search for a specific color, RGB model is not the best option the principal reason is the large changes between brightness and darkness of the color. The thresholds on the RGB color space are not useful in this case.
In HSV color space you have a channel to select the color of your interest and the other 2 channels are for the saturation and brightness of the color. But you can get accurate results only using the Hue channel (the first). The only thing that you need to take care about is in realize that you need to work this channel as a circular buffer because the maximum and minimum value are very similar in color, both are the red color.
Once you have the detected color you can set the new one and you can keep the saturation and brightness properties of the old color, by doing this the color changes will look like smoother.
If you wish to replace the colours of pixel in a 2D - Array you do so as following:
Array[x][y] = new value
where x and y stand for the location of the pixel, but keep in mind that images use the right-handed system thus the values of y grow bottom to top while in computers you use the left handed system so y values grow from top to bottom. The exact syntax of assigning the value of the new colour depends on the programming language you are using (the example above works in ruby). Also some programming languages already offer image manipulation functions built in so make sure to read the documentation to avoid implementing an already implemented function.
New Answer
New answer coming - now I understand that you mean the neighbours in the colour sense rather than the geometric sense...
You could calculate the vector colour distance from each pixel of your image to the colour you want to change and use that as a mask. So, if we create the same image as below... say we have a red-yellow gradient as background with a blue square on it and we wish to replace the central orange colour across the middle.
# Make red-yellow gradient with blue square within
convert -size 500x500 gradient:red-yellow -fill none -stroke blue -strokewidth 10 -draw "rectangle 100,100 400,400" image.png
Now clone that image, and fill the clone with the orange tone we want to replace, then calculate the vector colour distance from each pixel to that orange tone:
convert image.png \( +clone -fill "rgb(255,128,0)" -colorize 100% \) \
-compose difference -composite \
-evaluate Pow 2 -separate \
-evaluate-sequence Add -evaluate pow 0.5 \
-negate \
colour_distance.png
You can then use this colour_distance.png as a mask for alpha-compositing, so if we decide to replace that orangey tone with pink, we can do this:
convert image.png \
\( +clone -fill fuchsia -colorize 100% \) \
\( colour_distance.png -sigmoidal-contrast 20 \) \
-composite z.png
Note that I changed the pow 0.5 to pow 0.3 to roll off the mask more sharply.
Original Answer
Here's one way to do it. Say we have a red-yellow gradient as background with a blue square on it and we wish to replace the blue with green...
First, extract all the blue pixels onto a transparent background, then change them to green and blur them so they spread into the neighbouring pixels. Then overlay the blurred green pixels onto the original image.
I choose to do it with ImageMagick but you seem happy to adapt to other languages and libraries...
#!/bin/bash
# Make red-yellow gradient with blue square within
convert -size 500x500 gradient:red-yellow -fill none -stroke blue -strokewidth 10 -draw "rectangle 100,100 400,400" image.png
# Make everything blue green, then everything else transparent, then blur the lot
convert image.png -fill green -opaque blue -fill white +opaque green -transparent white -blur x6 x.png
# Now overlay the blurred greeness onto the original after replacing blues with green
convert x.png \( image.png -fill green -opaque blue \) -compose overlay -composite result.png
Image.png
x.png (the blurred, colour-replaced image)
result.png

Convert internal transparent pixels to white pixels

I've a number of images with transparent pixels both inside the graphics and outside. One example is:
and
So now I want to fill only the internal transparent pixels (the ones within the black boarder), is there any batch processing way to do it? I tried imagemagick convert tool, but didn't figure out how to the the "selective" conversion.
Thanks for any help!
there are following ways how can accomplish that (it all depends on how the image is composed):
if you know the part you want to keep transparents starts at 0x0
You start by removing all transparency with something like http://www.imagemagick.org/Usage/masking/#remove
and then re-add the transparency using flood-fill starting at 0x0 http://www.imagemagick.org/Usage/masking/#bg_remove
You have more control over whats going on by using a transparency mask:
then you start modifying the mask (which is now only black/white), by coloringusing floddfill starting wiht 0x0 (i.e. with blue), replacing the black with white and then replacing all blue pixel with black (for color replacement see http://www.imagemagick.org/Usage/color_basics/#replace)
# the mask
convert original.png -alpha extract mask.png
convert mask.png -fill blue -draw 'color 0,0 floodfill' mask_blue.png
convert mask_blue.png -fill white -opaque black mask_filled_blue.png
convert mask_filled_blue.png -fill black -opaque blue mask_filled.png
# change transparent to skyblue
convert original.png -background skyblue -alpha remove -alpha off original_nontransparent.png
# apply the modified mask
convert original_nontransparent.png mask_filled.png -alpha Off -compose CopyOpacity -composite final.png
Note that this techniqe works only moderatly well with half transparent things (see http://www.imagemagick.org/Usage/antialiasing/#floodfill ).
For better results with half-transparency you might want to use different methods to "fill" the mask. You would i.e. just draw a shape instead of the two fill operations on the mask:
convert mask.png -fill black -draw "circle 40,80 60,60" mask_filled.png
this will fill the center, but keep the half transparency intact.
if you know the "center" is always trnsparent, you could also floodfill from the center.
Hope this helps

Resources