Goal
I have hundreds of images that all look similar to this one here:
I simply want to use the green screen to create a mask for each image that looks like this one here (the border should preferably be smoothed out a little bit):
Here is the original image if you want to do tests: https://mega.nz/#!0YJnzAJR!GRYI4oNWcsKztHGoK7e4uIv_GvXBjMvyry7cPmyRpRA
What I've tried
I found this post where the user used Imagemagick to achieve chroma keying.
for i in *; do convert $i -colorspace HSV -separate +channel \
\( -clone 0 -background none -fuzz 3% +transparent grey43 \) \
\( -clone 1 -background none -fuzz 10% -transparent grey100 \) \
-delete 0,1 -alpha extract -compose Multiply -composite \
-negate mask_$i; done;
But no matter how I tweak the numbers, the results are not perfect:
I feel really dumb, that I cannot find a solution to such a simple problem myself. Also note, that I am using Linux. So no Photoshop or After Effects! :)
But I am sure that there has to be a solution to this problem.
Update 1
I've just tried using this greenscreen script by fmw42 by running ./greenscreen infile.jpg outfile.png and I am rather satisfied with the result.
But it takes around 40 seconds to process one image which results in a total 8 hours for all my images (although I have a rather power workstation, see specs below)
Maybe this has something to do witch those errors that occur while processing?:
convert-im6.q16: width or height exceeds limit `black' # error/cache.c/OpenPixelCache/3911.
convert-im6.q16: ImageSequenceRequired `-composite' # error/mogrify.c/MogrifyImageList/7995.
convert-im6.q16: no images defined `./GREENSCREEN.6799/lut.png' # error/convert.c/ConvertImageCommand/3258.
convert-im6.q16: unable to open image `./GREENSCREEN.6799/lut.png': No such file or directory # error/blob.c/OpenBlob/2874.
convert-im6.q16: ImageSequenceRequired `-clut' # error/mogrify.c/MogrifyImageList/7870.
convert-im6.q16: profile 'icc': 'RGB ': RGB color space not permitted on grayscale PNG `mask.png' # warning/png.c/MagickPNGWarningHandler/1667.
Workstation specs
Memory: 125,8 GiB
Processor: AMD® Ryzen 9 3900x 12-core processor × 24
Graphics: GeForce GTX 970/PCIe/SSE2 (two of them)
We know that the background is green and is distinguishable from the object by its color, so I suggest using color thresholding. For this, I have written a simple OpenCV Python code to demonstrate the results.
First, we need to install OpenCV.
sudo apt update
pip3 install opencv-python
# verify installation
python3 -c "import cv2; print(cv2.__version__)"
Then, we create a script named skull.py in the same directory with the images.
import cv2
import numpy as np
def show_result(winname, img, wait_time):
scale = 0.2
disp_img = cv2.resize(img, None, fx=scale, fy=scale)
cv2.imshow(winname, disp_img)
cv2.waitKey(wait_time)
img = cv2.imread('skull.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of green color in HSV
lower_green = np.array([70, 200, 100])
upper_green = np.array([90, 255, 255])
# Threshold the HSV image to extract green color
mask = cv2.inRange(hsv, lower_green, upper_green)
mask = cv2.bitwise_not(mask)
#cv2.imwrite('mask.png', mask)
show_result('mask', mask, 0)
cv2.destroyAllWindows()
You can easily find a tutorial about HSV color operations using OpenCV. I will not go over the functions used here, but one part is important. Image operations are generally done in RGB color space, which holds red, green and blue components. However, HSV is more like human vision system which holds hue, saturation and value components. You can find the conversion here. Since we seperate color based on our perception, HSV is more suitable for this task.
The essential part is to choose the threshold values appropriately. I chose by inspection around 80 for hue (which is max. 180), and above 200 and 100 for saturation and value (max. 255), respectively. You can print the values of a particular pixel by the following lines:
rows,cols,channels = hsv.shape
print(hsv[row, column])
Note that the origin is left upper corner.
Here is the result:
Two things may be needed. One is doing the operation for a set of images, which is trivial using for loops. The other is that if you do not like some portion of the result, you may want to know the pixel location and change the threshold accordingly. This is possible using mouse events.
for i in range(1, 100):
img = imread(str(i) + '.jpg')
def mouse_callback(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN:
row = y
column = x
print(row, column)
winname = 'img'
cv2.namedWindow(winname)
cv2.setMouseCallback(winname, mouse_callback)
Keep in mind that show_result function resizes the image by scale factor.
If you do not want to deal with pixel positions, rather you want smooth results, you can apply morphological transformations. Especially opening and closing will get the work done.
kernel = np.ones((11,11), np.uint8)
opened = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
closed = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
Result with opening (kernel=11x11):
I can't really fit this in a comment, so I've put it as an answer. If you want to use Fred's greenscreen script, you can hopefully use GNU Parallel to speed it up.
Say you use the commands:
mkdir out
greenscreen image.png out/image.png
to process one image, and you have thousands, you can do the following to keep all your CPU cores busy in parallel till they are all processed:
mkdir out
parallel greenscreen {} out/{} ::: *.png
If on a Unix-like system, you can try my greenscreen script that makes calls to ImageMagick and is written in Bash Unix. For example:
Input:
greenscreen img.jpg result.png
Result (green turned transparent):
The result has been reduced in size by 50%, just so that StackOverflow will not object to the original result being too large. However, StackOverflow has changed the image from transparent PNG to white background JPG.
Note that other images, may need values for the arguments other than the defaults. You can get my script at http://www.fmwconcepts.com/imagemagick/. Note that for commercial use, you will need to contact me about licensing.
Related
I have images of old paintings. The paintings are old and dusty with faded colours as shown here.
How do I give any image this type of an 'old' appearance? I couldn't find any filters or openCV functions to achieve this type of look?
EDIT: My question is different as the other one solves the problem using the sepia filter and using the grain effect. I'm not looking for that sort of an appearance. I want my image to look like an old damaged painting. This means that the colours should be faded and it should have an overall dusty appearance.
There's no real need to write any code and use OpenCV, since you can do all that on the command-line with ImageMagick which is installed on most Linux distros and is available for macOS and Windows.
First, fading. This can be simulated by reducing the saturation of an image. So if we start with this Mona Lisa image:
We can fade her using this command to leave the brightness unchanged at 100% of its original value and reduce the saturation to 50% of its original value. I am intentionally "over-egging" everything so you can see it clearly. You should maybe be more subtle.
convert mona.jpg -modulate 100,50 result.jpg
Next, vignetting - or dark corners. You can use something like this:
convert mona.jpg \
\( +clone -fill white -colorize 100 -background "gray(50%)" -vignette 0x15+1+1% \) \
-compose multiply -composite result.jpg
The 0x15 controls the roll-off, or how gradual the change is, so increase the 0x15 if you want a smoother roll-off or go down to 0x5 if you want it harder. The +1+1% means that the ellipse will be 1% smaller than the width of the image and 1% smaller than the height of the image. So if you want a smaller light hole and bigger dark corners, go for +10+10%. The degree of darkening is controlled by the gray(50%) so you can diddle with that till you are happy too :-)
Finally, dust. Best thing is to get a PNG image of some dust, resize it to match the size of your image and overlay it.
First get the size of Mona:
identify mona.jpg
mona.jpg JPEG 403x600 403x600+0+0 8-bit sRGB 57130B 0.000u 0:00.000
So, she is 403x600. Here is a sample of some dust - again, you can be more subtle - I am just being heavy-handed so it shows:
Let's resize the dust to match and overlay it:
convert mona.jpg \( dust.png -resize 403x600\! \) -composite result.jpg
Then you can combine all three effects, fading, vignetting and dust, into a single command:
convert mona.jpg -modulate 100,50% \
\( +clone -fill white -colorize 100 -background "gray(50%)" -vignette 0x15+1+1% \) \
-compose multiply -composite \
\( dust.png -resize 403x600\! \) -composite result.jpg
If you have lots of images to process, you can script the whole lot to be done in parallel very easily with GNU Parallel - see some of my other answers for examples.
Keywords: artificial ageing, image ageing, command-line, command line, ImageMagick, magick, old, old photo, photo effect, convert, dust, scratches, fading, faded.
I would suggest using a style transfer tool, rather than manually coming up with a procedure to mimic the style of an old painting. There are plenty free style transfer tools and libraries available.
I would suggest using OpenCV various filters to create the effect you need. You have to try various filters and try to figure what works for you, But I have suggestions which you can try.
For color, fading try Erode and Dilate with small kernel size.
Next, add some noise, Salt and Pepper will do just fine, also try gaussian filter after applying noise. Salt and Pepper is non-linear noise and Gaussian is a linear filter so it will just spread the noise, but keep the filter kernel small.
Try finding some images of dust, torn page edges (WITHOUT BACKGROUND) like in the following link:
https://www.google.co.in/searchq=dust+png+images&newwindow=1&rlz=1C1CHBF_enIN797IN798&source=lnms&tbm=isch&sa=X&ved=0ahUKEwjtq7ZvfPcAhXJO48KHQ2UD0kQ_AUICigB&biw=1536&bih=759#imgrc=_
Keeping alpha transparency in mind, mask these over your images.
With all the things in correct proportion and sequence, You will get your old dusty image.
I have two questions:
Firstly, how to detect the area of bar code target in an image (like the sample images), which may have a few noises.
Secondly, how to efficiently do the detection, for instance, in 1/30 seconds.
Squash (resize) the image till it is only 1 pixel tall, then normalise it to the full range of 0-255 and threshold. I am using ImageMagick at the command-line here - it is installed on most Linux distros and is available for OSX and Windows also with Python, PHP, Ruby, C/C++ bindings.
convert barcode.png -resize x1! -scale x10! -normalize -threshold 50% result.png
I have then scaled it to 10 pixels tall so you can actually see it on here - but you would keep the original width and have a height of one pixel. Then just find the first white pixel in your single row of pixels.
Your recently added, smaller barcode gives this:
I have two versions of a same image one is original image and one is smoothed version of it. I want to know how much edge information is contained in both images as numerical value not as an image like Perceptual quality metric etc. Is there any method to calculate edge information.
You can do this easily from the commandline with Imagemagick which is installed on most Linux distros and available for OSX and Windows.
First convert to grayscsle then do a Canny Edge detection then count the white pixels.
I'm not at a proper computer, just an iPhone, so I can't check but it will look like this:
convert image.jpg -colorspace gray \
-canny 0x1+5%+10% \ \
\( +clone -evaluate set 0 \) \
-metric AE -compare \
-format "%[distortion]" info:
287
Remove the last 3 lines and replace with a simple image filename to see the resulting edge detected image rather than count the white pixels.
Divide the number of white pixels by the product of the image height and width to normalize results for differently sized images.
There is no absolute measure for edge information, since what constitutes an edge depends on the thresholds you apply on the luminance-gradients. What I would do is to consider the distribution of gradients (take e.g. Sobel Operator in x- and y-direction so that the magnitude in each point is then sqrt(Gradient_x^2 + Gradient_y^2). From the distibution of Gradients, you can then use a quantile (e.g. 70% which is a typical value in the Canny case), which is fore sure lower for a smoothed Picture than for an unsmoothed.
Consider two images. The size of these two images could be anything. Bring size of those two images same. Develop an algorithm to mix these two images, such that alternate pixels are brought from two image courses. It is fusion of two images. For example, pixel 1 is image1’s, pixel 2 is from image 2, 3rd pixel from image 1 and so on like that……
I know you prefer to use Matlab, but until someone gives you a Matlab answer, you may like to play around with ImageMagick which can do this for you and is in most Linux distributions anyway and available for free for Windows and Mac OSX.
First, let's create 2 images of different sizes and colours:
convert -size 300x300 xc:blue image1.png
convert -size 200x400 xc:red image2.png
Basically, you can resize images as you read them in by specifying the image size in square brackets after the filename, so I am arbitrarily choosing to resize both images to 256x256 pixels. Then I use the extremely powerful fx operator, so detect if I am processing an odd or an even numbered pixel, and choose either from the first or the second image accordingly:
convert image1.png[256x256] image2.png[256x256] -fx "i%2?u:v" out.png
Here is a way to do it with MATLAB.
clear
clc
%// Initialize red and blueimages
RedImage = zeros(300,300,3,'uint8');
BlueImage = zeros(200,400,3,'uint8');
%// Color them
RedImage(:,:,1) = 255;
BlueImage(:,:,3) = 255;
figure('Color',[1 1 1]);
%// Show them
subplot(1,2,1)
imshow(RedImage)
subplot(1,2,2)
imshow(BlueImage)
It looks like this:
%// Resize them to same size
RedImage = imresize(RedImage,[256 256]);
BlueImage = imresize(BlueImage,[256 256]);
%// Initialize new image
NewImage = zeros(256,256,3,'uint8');
%// Assign alternate pixels to new images
NewImage(1:2:end,1:2:end,:) = RedImage(1:2:end,1:2:end,:);
NewImage(2:2:end,2:2:end,:) = BlueImage(2:2:end,2:2:end,:);
figure
imshow(NewImage)
Which outputs this:
It looks dark but resize the figure will show you that it works indeed!
Hope that helps! Have fun.
OK, so I have a folder of like 16 images all between the dimensions of 205x150 to 103x148. I want to size them down to the pixel height and width of 25px and stack them horizontally on a transparent background... is that possible?
I should probably be using ImageMagick for this...
You can do all that with ImageMagick.
You're question is not very specific, so here's a quick cheat sheet of command examples that may help you:
# resize image to width 25, keeping aspect ratio
convert -geometry 25x src/image1.png out/image1.png
# resize image to height 25, keeping aspect ratio
convert -geometry x25 src/image1.png out/image1.png
# concatenate images horizontally
convert +append src/image1.png src/image2.png out/image12horiz.png
# concatenate images vertically
convert -append src/image1.png src/image2.png out/image12vert.png
In addition, the montage command is probably perfect to create the final image you are looking for (on a transparent bg with some padding, etc), but I don't remember the syntax.
Another useful command is identify, to find the dimensions of images (along with other details).
After you install ImageMagick, you can see the list of commands in man ImageMagick, and get further details on each command in the man pages. There are an awful lot of functions, but it should not be too difficult to figure out the rest on Google. (I do that all the time.)
Just to add something to #janos answer.
I haven't used previous versions of ImageMagick but on version v6 or later according to the docs http://www.imagemagick.org/Usage/resize/#geometry
Geometry is a very special option. The operator behaves slightly differently in every IM command, and often in special and magical ways. The reasons for this is mostly due to legacy use and should be avoided if at all possible.
So other than the -geometry parameter you can still use -resize and omit the value you want in order to keep the aspect ratio. You can also use the -quality parameter to avoid image quality downgrade when resizing them. Value of quality is between 1 (lowest image quality and highest compression) and 100 (best quality but least effective compression). You can read more here: https://imagemagick.org/script/command-line-options.php#quality
For example:
# resize image to height 25, keeping aspect ratio with quality 90
convert -resize x25 original_image.jpeg -quality 90 resized_image.jpeg