ImageMagick Unable to change colour in shade explicitly for image - image-processing

I want to use ImageMagick to change colour in shade.
I am able to manage the shade change using :
convert input.png -colorspace HCL -channel R -evaluate set 5% +channel -colorspace sRGB output.png
Using set XX% i am able to get different colours like, red, green, yellow, blue, pink, sky-blue, gray, etc.
The below command works for targeting blue colour :
convert input.png -colorspace HCL -channel R -separate +channel -level 48,52% output.png
But I am unable to target other colour explicitly.
For example, if I want to change green colour with some other colour, resulted image will effect green, yellow,red and sky-blue as well.
Is there a way to explicitly change a single colour in shade for :
yellow
sky-blue
pink
green
white
black
red
I tried changing all -channel : R,G,B,C,M,Y,K,A,O.
Using -separate option I can target RBG, but the problem with RGB is R effect red, yellow and pink, G effect green, sky-blue and yellow and B effect blue, pink and Sky-blue.
sample for output :
RGB image colour change
expected output : In the above output for "output-0" it effect red,yellow and pink. i want the command which will effect only red. similarly for other colours as well.
links I used : https://www.imagemagick.org/discourse-server/viewtopic.php?t=33361
I am using python to run this command. I am also open to use other libraries which will work with all the colours explicitly.

If your image is representative like I requested, it is as simple as this:
magick rgb.png -fill white -opaque red result.png
If you also want to affect hues "close to red", you can apply some fuzz:
magick rgb.png -fuzz 40% -fill white -opaque red result.png
Notice that also affects the edges of the red circle where it is a "feathered red".
If not, your ImageMagick code is essentially doing a "Hue rotation" and, as you have noticed, it affects the entire image. Read the Wikipedia page on HSV before continuing. Here is an HSI Hue wheel for reference:
The solution is to do your Hue rotation, but apply its effects via a mask that only selects the colours/areas you want affected. Remember that OpenCV halves the Hue from the range 0..360 to 0..180 so that it can store a Hue in a np.uint8.
So, if we load the same image as above and select only the greens (where Hue is near 120) we can rotate just those into blues by adding 60 (Hue=240):
#!/usr/local/bin/python3
import cv2 as cv
import numpy as np
# Load the image and convert to HSV colourspace
image = cv.imread("rgb.png")
# Convert to HSV and split channels
hsv=cv.cvtColor(image,cv.COLOR_BGR2HSV)
H,S,V = cv.split(hsv)
# Shift only greens (Hue near 120) around hue circle by 120 degrees to blues - remembering OpenCV halves all these values - see comment
H[(H>55)&(H<65)] += 60
# Recombine into single 3-channel image and convert back to RGB
result = cv.merge((H,S,V))
result = cv.cvtColor(result,cv.COLOR_HSV2BGR)
cv.imwrite("result.png",result)
If you want to change the blues (Hue=240) into yellows (Hue=60), just change this:
H[(H>55)&(H<65)] += 60
into this:
H[(H>115)&(H<125)] -= 90
If you want to broaden the range of greens affected, decrease the 55 in my code and/or increase the 65. If you want to move greens to a different hue, either increase or decrease the 60.
You can do all the stuff above with PIL/Pillow if you want to - you don't need to install the (massive) OpenCV.
Keywords: Image, image processing, Python, OpenCV, ImageMagick, Hue, HSL, HSV, hue rotation, colour replacement, selective colour, mask.

Related

Set alpha according to gradient with imagemagick

Is it possible to set the alpha channel of an image according to a gradient with ImageMagick?
I'd like the pixels on the left border of an image to be 100% transparent, and the ones on the right border to be 100% opaque, and with the ones in the middle having progressively lower transparency values.
Or in a more general case - given a grayscale image, set the alpha channel of another image as a function of the B&W values (black = 100% alpha, white 0% alpha).
With ImageMagick you can use -sparse-color to apply a gradient only to the alpha channel to get the result you describe.
convert in.png -alpha set -background none -channel A \
-sparse-color barycentric "0,0 none %[w],0 white" +channel out.png
That command starts by activating the alpha channel and setting the background color to transparent. Then it uses -channel A to apply the following operation only to the alpha channel. The -sparse-color operation tells it to start with transparent at the far left edge, pixel 0,0 and graduate to opaque at pixel %[w],0. The %[w] means the width or far right edge.
Although there are many ways to accomplish the effect you've described, by using -sparse-color you can easily make the gradient start and end at any positions on the image without having to create any intermediate masking images.
Simple. You would use -composite CopyOpacity to set the alpha channel from a gradient mask.
Given I have the following images. image.png & transparent_mask.png
We can set the image transparency (where black is alpha, and white is opaque) by copying values from the mask image to the input image alpha channel.
convert image.png transparent_mask.png -compose CopyOpacity -composite output.png

Can I separate image on arbitrary colors?

Is it possible to separate an image into channels based on arbitrary colors? If I have an image that's visually pink, white and black, is it possible to create 3 separate images describing the "pinkyness" "whiteness" and "blackness" channels?
My purpose is to mutate - let's say - pink into yellow, black into blue and white into red.
Ideally this should be possible with ImageMagick
I would use color substitution but it would substitutes all colors close to pink with one single colors, whereas I want to preserve the different levels of intensity of pink.
Using this image as an example, I'd like to turn the knit part rom pink/white/black into blue/yellow/green.
You need to use the hue channel from HCL or HSL, etc and mask and swap hues. I have a bash Unix shell script, replace color, for Imagemagick that will do that.
Input:
replacecolor -i "#BA3A67" -o blue Adidas-pink.png Adidas-blue.png
#BA3A67 is your input pinkish color that I measured. Blue is the desired output color. Any opaque color can be use and specified as a color name or hex value or rgb(rr,gg,bb) triplet.
The script cannot change gray tones (black, gray, white), since the hue is the same as red. But you can still change black and/or white (or any color) in imagemagick using:
convert image.suffix -fuzz XX% -fill newcolor -opaque oldcolor output.suffix
Where XX is some percent tolerance.
See http://www.fmwconcepts.com/imagemagick/replacecolor/index.php

Apply gradient mask on image that already has transparency with ImageMagick?

I've got a PNG image with transparency:
original.png
Now I want to use ImageMagick to apply a diagonal gradient to its alpha channel. I mean so that its opacity remains in the top left corner, and gradually fades out to completely transparent in the bottom right corner. Like this:
result.png
So basically I want to generate a gradient, and use that as a mask for the image. But the image already has an alpha channel (transparency) of its own. Here's a visualisation of what I'm trying:
(original and result here displayed on checkerboard for visiblity, but I mean actual transparency)
I think I understand how to generate a diagonal gradient (the barycentric gradient command is very useful for this). But this creates a gradient in the color channels i.e. a colored or grayscale gradient. Whereas I want to apply the gradient on the alpha channel.
From the IM manual I understand the -compose CopyOpacity operator could be used for this. However this seems to copy the alpha from the mask on to my image. I need to "apply" this gradient color on my existing alpha channel, so basically I need my image's alpha channel to be multiplied by the grayscale color from the gradient image.
What would be the correct IM command line to perform the operation displayed above?
Here is one way you could do it:
convert tree.png -write MPR:orig -alpha extract \
\( +clone -colorspace gray -fx "1-j/h" \) \
-compose multiply -composite -write alpha.png \
MPR:orig +swap -compose copyopacity -composite result.png
The -write alpha.png can be omitted - it just shows the alpha layer for debugging and illustration purposes.
The MPR is just a temporary copy of the original image that I hold in memory while I am dinking around with the alpha channel and which I bring back near the end. The gradient in the alpha channel is generated by the -fx and I made the colorspace gray first so it only has to run once, instead of three times.
If you knew the dimensions of the tree image up front, you could replace the part in parentheses with:
-size WxH gradient:black-white
but I don't know the dimensions up front and I don't want a second convert command to get them, so I basically clone the original image's alpha channel to get a canvas the right size and fill it in with -fx.

PIL - Image processing - how to achieve a clean image with no noisy background?binarization step exagerated?

Good afternoon,
I am writing an ocr program to detect text on images. So far I am getting good results but when text is black and background is white. What can I do to improve images that have white text on light colored background (yellow, green, etc)?
One original example image could be:
So far I am just converting it to grey_scale using:
image = image.convert('L')
Then apply a series of filters like for example:
SHARPEN
SMOOTH
BLUR
etc
Then i do binarization like this:
image = image.point(lambda x: 0 if x<128 else 255, '1') #refers to http://stackoverflow.com/questions/18777873/convert-rgb-to-black-or-white and also to http://stackoverflow.com/questions/29923827/extract-cow-number-from-image
My outoup images are indeed very bad for ocr feeding like this one:
What am I doing wrong? What should be the best approach for white text on light colored background?
Another doubt: is my binarization step to strong/exagerated?
Should I mix some filters? Could you suggest some?
PS: I am a total newbie to image processing, so please keep it simple =x
Thanks so much for your attention and help/advices.
I tried this with ImageMagick, which has Python bindings too - except I did it at the command line. I guess you can adapt what I did quite readily - I don't speak Pythonese nor use PIL but hopefully it will give you some insight as to a possible avenue.
convert http://i.stack.imgur.com/2cFk3.jpg -fuzz 50% -fill black +opaque white -threshold 50% x.png
Basically it takes any colour that is not within 50% of white and fills it with black, then it thresholds the result to pure black and white.
Another option would be to threshold the image according to the saturation of the colours. So, you convert to HSB colorspace, separate the channels and discard the hue and brightness. You are then left with the saturation which you threshold as follows:
convert http://i.stack.imgur.com/2cFk3.jpg -colorspace hsb -separate -delete 0,2 -threshold 50% x.png
Throw in a -negate to get white letters on black.
I have copied some other code for PIL, and am modifying it kind of/sort of to something that may be close to what you need - bear in mind I know no Python:
import colorsys
from PIL import Image
im = Image.open(filename)
ld = im.load()
width, height = im.size
for y in range(height):
for x in range(width):
r,g,b = ld[x,y]
h,s,v = colorsys.rgb_to_hsv(r/255., g/255., b/255.)
if s>0.5: // <--- here onwards is my attempted Python
ld[x,y] = (0,0,0)
else:
ld[x,y] = (255,255,255)

Creating a more effective matt for chromakey?

I'm trying to chromakey some pictures. Here is an example of one:
Here is another one,
Now using image magic, I can generate a mat like this..
But I can never get the mat to be "full". My plan is to create a static mat for the turntable and the lightbank -- those won't have to be removed. But, I'd like to fix the problems I'm seeing with the grill, licenseplate, and window. I'd like the car to show up pitch-black. I'm using ImageMagick's convert to get this working,
convert 1.bmp -channel g -separate +channel -fuzz 45% -fill black -opaque black -fill white +opaque black greenscreensample_mask_1.gif
How can I improve this to fill in the bumper of the vehicle?
I would guess the shinny parts are slightly green and you could try reducing the fuzz value.
You can use the -fx operator and then work with specific channels. The following is by no means optimal, and also, it is very inefficient to execute:
convert ./in.jpg -background none -transparent white -channel Alpha -fx '1-((g-r)+(g-b)+(g-(r+b)/2))^2' -channel Green -fx '(r+b)/2' ./out.png;eog ./out.png
in order to obtain a key for the green channel you can subtract the
red from the green
blue from green
average of blue and red channels from the green channels
the very basic colour correction involves replacing fringed areas with the average of the blue and red channels, here however the entire image had its green channel replaced with the average of the blue and red channels. you should actually write an algorithm that seperates the fringe into a seperate channel, then you colour correct the entire image and mix it in with the original based on this "fringe" matt.
thankyou, best of luck

Resources