Replace white coloured rows in image using python - python

I am trying to trim parts of the image where a complete row of Image doesn't have anything except white color.
I tried using matplot lib
convert image into matrix and saw if (r,g,b) = (0,0,0) or (1,1,1) and removed entire row in image if every (r,g,b) is of above kind in the row
matrix looks like [ [ [r,g,b], [r,g,b]....] ],...., [ [r,g,b], [r,g,b]....] ] ]
i achieved my requirement but i am running this for around 500 images and it is taking 30 minutes around. Can i do it in better ways?
and the required image should be like
Edit-1 :
tried with trim method from wand package
with wand_img(filename=path) as i:
# i.trim(color=Color('white'))
# i.trim(color=Color('white'))
i.trim()
i.trim()
i.save(filename='output.png')
but not working for the following type of images

You could use ImageMagick which is installed on most Linux distros and is available for macOS and Windows.
To trim one image, start a Terminal (or Command Prompt on Windows) and run:
magick input.png -fuzz 20% -trim result.png
That will give you this - though I added a black border so you can make out the extent of it:
If you have lots to do, you can do them in parallel with GNU Parallel like this:
parallel -X magick mogrify -trim ::: *png
I made 1,000 copies of your image and did the whole lot in 4 seconds on a MacBook Pro.
If you don't have GNU Parallel, you can do 1,000 images in 12 seconds like this:
magick mogrify -trim *png
If you want to do it with Python, you could try something like this:
#!/usr/bin/env python3
from PIL import Image, ImageChops
# Load image and convert to greyscale
im = Image.open('image.png').convert('L')
# Invert image and find bounding box
bbox = ImageChops.invert(im).getbbox()
# Debug
print(*bbox)
# Crop and save
result = im.crop(bbox)
result.save('result.png')
It gives the same output as the ImageMagick version. I would suggest you use a threading tool to do lots in parallel for best performance.
The sequential version takes 65 seconds for 1,000 images and the multi-processing version takes 14 seconds for 1,000 images.

Using two trims in Imagemagick 6.9.10.25 Q16 Mac OSX Sierra works fine for me. Your image has a black bar on the right hand side. The first trim will remove that. The second trim will remove the remaining excess white. You may need to add some fuzz (tolerance) amount for the trim. But I did not need it.
Input:
convert img.png -trim +write tmp1.png -trim result.png
Result of first trim (tmp1.png):
Final Result after second trim:
ADDITION:
Looking at the docs for Python Wand:
trim(*args, **kwargs)
Remove solid border from image. Uses top left pixel as a guide by default, or you can also specify the color to remove.
Parameters:
color (Color) – the border color to remove. if it’s omitted top left pixel is used by default
fuzz (numbers.Integral) – Defines how much tolerance is acceptable to consider two colors as the same.
You will need to specify color=black for the first trim, since this version of trim uses the top left corner for trimming. Command line Imagemagick looks at all corners. If that fails, then add some fuzz value.

Related

Linux: How to merge two pictures in Gimp with Python-Fu

I have to acomplish the following task: Two square, equally sized png images have to be put together side by side and exported as a combined image. This has to be done to hundreds of pairs in a folder with endings "_1" and "_2"
I think this can be done in Gimp with Pytho-Fu, but trying to understand the fundamentals of scripting for Gimp is a bit overwhelming on a tight schedule and I really just need a solution for this single task. I would really appreciate you to point me in the right direction with this.
(If there is a simpler solution than using Gimp, please let me know. It should run on Linux and ideally be able to be executed from bash.
After xenoid's recommendation:
I found ImageMagick's syntax and documentation to be a horrible mess less than optimal, so I'll share how I got it done:
with Ubuntu 18.04.04:
montage -tile x1 -geometry +0+0 input1.png input2.png output.png
The whole thing (probably not interesting for anyone else...)
#! /bin/bash
input="./Input/"
output="./Output/"
# add to output filename
prefix="CIF_"
postfix="_2"
# get file list
readarray -d '' RRA < <(find $input -regextype posix-egrep -regex '(.*)?_1_cr\.png$' -print0)
echo "Merging ${#RRA[#]} images.."
# remove directory from filename
RRA=( "${RRA[#]##*/}" )
# strip last part of filename: "_1_cr.png"
RRA=( "${RRA[#]/%_1_cr\.png/}" )
# merge images
for fall in "${RRA[#]}";do
# check if there are two images to merge for current case
if test -f "$input${fall}_2_cr.png"; then
echo "${fall}"
montage -tile x1 -tile-offset +10 -geometry +0+0 -border +20+20 -bordercolor white $input${fall}_1_cr.png $input${fall}_2_cr.png $output$prefix${fall}$postfix.png
else
echo "${fall} - no second image found"
fi
done
With ImageMagick, you can loop over each pair and just do:
convert image_1.png image_2.png +append image_1_2.png
See
https://imagemagick.org/Usage/layers/#append
If you want space between them, then use +smush X, where X is the amount of space that you want. If you want them overlapped, then use a negative value for X. You can set the color of the space using -background color.
See
https://imagemagick.org/script/command-line-options.php#smush

GIMP Python Plugin to load 2 images as layers

I'm trying to make a plugin for gimp that opens two images as separate layers and transforms one of them (more on that below). I'm using GIMP 2.10.12.
I've been struggling to find a proper complete documentation for GIMP's Python interface and am mostly just working from what code snippets I've been able to find. This is what I have so far:
#!/usr/bin/env python2
import os
from gimpfu import *
def load_pair(img_f):
mask_f = img_f.replace(IMG_DIR, PRED_DIR)
result_f = os.path.splitext(img_f.replace(IMG_DIR, SAVE_DIR))[0]
result_dir = os.path.dirname(result_f)
if not os.path.isdir(result_dir):
os.makedirs(result_dir)
img = gimp.Image(100, 100)
pdb.gimp_display_new(img)
for f, name, pos in ((img_f, "Image", 0), (mask_f, "Mask", 1)):
layer = pdb.gimp_file_load_layer(img, f)
pdb.gimp_layer_set_name(layer, name)
pdb.gimp_image_insert_layer(img, layer, None, pos)
register(
"python_fu_open_image_pair",
...,
"<Toolbox>/Image/Open Image Pair",
"",
[(PF_FILE, "img_f", "Image:", None)],
[],
load_pair
)
main()
This kind of does what I want but with a couple of problems.
Question 1
Currently I'm using gimp.Image(100, 100) to open a new image. This means I have to then Fit Canvas to Layers and adjust the zoom and position every time I load a new image pair.
Is there a way to find an image's size from pdb before opening it or do I have to use another library (like PIL) for this? I'm trying to keep my plugin's dependencies to a minimum.
The two images are guaranteed to have the same size.
Since File->Open automatically adjusts the canvas to the image size, I would hope there'd be a nice way to achieve this.
Question 2
I would like to automatically create and set the current working file to result_f + '.xcf' (see above code) - such that File -> Save would automatically save to this file. Is this possible in pdb?
Question 3
Most importantly, I currently have the Mask images saved as black-and-white images. Upon loading a mask as a new layer, I'd like to transform the black colour to transparent and white colour to green (0,255,0). Additionally, since they are saved as .jpg images, the white and black aren't necessarily exactly 255 and 0 intensities but can be off by a bit.
How do I do this automatically in my plugin?
The good way would be to load the first image normally, and the rest as additional layers. Otherwise you can reset the canvas size (pdb.gimp_image_resize(...)) once you have loaded all the layers, and then create the Display.
You can give a name and a default file to the image by setting image.name and image.filename.
To convert the white to green use pdb.plug_in_colors_channel_mixer(...) and set all the gains to 0., except green in green. Make the black transparent use pdb.plug_in_colortoalpha(...).
PS: For color2alpha:
import gimpcolor
color=gimpcolor.RGB(0,255,0) # green, integer args: 0->255)
# or
color=gimpcolor.RGB(0.,1.,0) # green, floating point args (0.->1.)
pdb.plug_in_colortoalpha(image, layer, color)
The Python doc is a direct copy of the Scheme one. In Python, the RUN-INTERACTIVE parameter is not positional, so it doesn't appear in most calls, if you need it, it is a keyword parameter.

detect and select nonblack images in a folder

I am currently working on a media project. We've shooted looong clips, mainly dark if not black. I have decomposed these clips into their frames (>500k single frames) and put them in some folders. Now, my goal is to find out and select those frames that are not black or mainly dark: it's around a thousand out of the total.
This seems a job that a simple Python script can handle without too much effort. I know that scikit-image is quite common to work with images, but don't know how to come up with a script that does the job neatly. I have some experience with scientific programming but this with images manipulation is a bit out of my field.
For example, this image should be reported as black and thus ignored, while this other one, although in low light, should be kept as good.
Ideally, it would be optimal to have a script that uses one or more criteria to determine if an image is totally dark or not, and in the latter case put it into another folder for human (me) inspection.
Any help is exteremely appreciated!
You can get the mean of each image very simply without writing any code using ImageMagick which is available for Windows, Linux and macOS.
Like this:
magick identify -format '%[fx:mean*255] %f\r\n' black.jpg
1.01936 black.jpg
and:
magick identify -format '%[fx:mean*255] %f\r\n' nonblack.jpg
1.72921 nonblack.jpg
To improve performance, I would use GNU Parallel on macOS or Linux, but in Windows, I would open a new command prompt for each directory and run several scripts in parallel, or start one script processing all the files ending in 0 or 1, a second one processing files ending in 2 or 3, a third one processing files ending in 4,5 or 6 and a final one processing files ending in 7,8 or 9.
If I was doing it in Python I would use a multiprocessing pool to speed things up, by the way.
Opencv is enough to solve this problem.
use np.mean(image, axis=2) to get mean of different channels, then you can easily check the black ones.
As pointed out in the replies, taking a 'mean' of the image helped. After reading in the image, I compute np.mean(img, axis = 2).mean() so that I have the mean of the three colour channels. If this mean is low (<2) then the image is discarded, otherwise the file is copied to another folder.
The code is not really time efficient as it takes ~3 hours for 200k files, but does the trick!
You'll probably want to use PIL (Python Image Library).
I did a quick search for code that calculates the average of an image and found this snippet:
Image Average Color
import Image
def get_average_color((x,y), n, image):
""" Returns a 3-tuple containing the RGB value of the average color of the
given square bounded area of length = n whose origin (top left corner)
is (x, y) in the given image"""
r, g, b = 0, 0, 0
count = 0
for s in range(x, x+n+1):
for t in range(y, y+n+1):
pixlr, pixlg, pixlb = image[s, t]
r += pixlr
g += pixlg
b += pixlb
count += 1
return ((r/count), (g/count), (b/count))
image = Image.open('test.png').load()
r, g, b = get_average_color((24,290), 50, image)
print r,g,b
Maybe you could just iterate through all of the images in your folder and log (or copy) ones that are above a certain values.
There's probably a more elegant way to do this using PIL but maybe this will get you started.
Hope it helps!

Converting raw bytes to image

I have a file that contains a 240x320 image but its byte format I opened it in a hex editor and got what something like an array 16 columns 4800 raw.
Im completely new to this thats why im facing trouble I have tried using a python script but it gave an error on line 17, in data = columnvector[0][i]:
IndexError: list index out of range.
I have tried a java code but that was an error as well, I wanted to try some c# codes but none of the codes i found explains how i can feed my file to the code. This is the python code
import csv
import sys
import binascii
csv.field_size_limit(500 * 1024 * 1024)
columnvector = []
with open('T1.csv', 'r') as csvfile:
csvreader = csv.reader(csvfile,delimiter=' ', quotechar='|')
for row in csvreader:
columnvector.append(row)
headers =['42','4D','36','84','03','00','00','00','00','00','36','00','00','00','28','00','00','00',
'40','01','00','00','F0','00','00','00','01','00','18','00','00','00','00','00','00','84','03','00','C5','00',
'00','00','C5','00','00','00','00','00','00','00','00','00','00','00']
hexArray=[]
for i in range(0,76800):
data = columnvector[0][i]
hexArray.extend([data,data,data])
with open('T1.txt', 'wb') as f:
f.write(binascii.unhexlify(''.join(headers)))
f.write(binascii.unhexlify(''.join(hexArray)))
I want to convert the file to an image using any method I honestly don't care what method to use just as long as it gets the job done.
this is some the files
https://github.com/Mu-A/OV7670-files/tree/Help
You can make the binary data into images without writing any Python, just use ImageMagick in the Terminal. It is included in most Linux distros and is available for macOS and Windows.
If your image is 320x240 it should be:
320 * 240 bytes long if single channel (greyscale), or
320 * 240 * 3 if 3-channel RGB.
As your images are 76800, I am assuming they are greyscale.
So, in Terminal, to make that raw data into a JPEG, use:
magick -depth 8 -size 320x240 gray:T1 result.jpg
or, if you are using version 6 of ImageMagick, use:
convert -depth 8 -size 320x240 gray:T1 result.jpg
If you want a PNG with automatic contrast-stretch, use:
magick -depth 8 -size 320x240 gray:T1 -auto-level result.png
Unfortunately none of your images come out to anything sensible. Here is T1, for example:
The histograms do look somewhat sensible though:
I think you have something fundamentally wrong so I would try reverting to first principles to debug it. I would shine a torch into, or point the camera at a window and save a picture called bright.dat and then cover the lens with a black card and take another image called dark.dat. Then I would plot a histogram of the data and see if the bright one was at the rightmost end and the dark one was at the leftmost end. Make a histogram like this:
magick -depth 8 -size 320x240 Gray:bright.dat histogram:brightHist.png
and:
magick -depth 8 -size 320x240 Gray:dark.dat histogram:darkHist.png
for i in range(0,76800):
is a hardcoded value, and because columnvector[0][i] does not have that many values, you get that IndexError: list index out of range.
Consider why you need to set your range from 0-76800 or if the value can be dynamically sourced from len() of something.
Another simple way to make an image from a binary file is to convert it to a NetPBM image.
As your file is 320x240 and 8-bit binary greyscale, you just need to make a header with that information in it and append your binary file:
printf "P5\n320 240\n255\n" > image.pgm
cat T1 >> image.pgm
You can now open image.pgm with feh, Photoshop, GIMP or many other image viewers.

Use Python / PIL or similar to shrink whitespace

Any ideas how to use Python with the PIL module to shrink select all? I know this can be achieved with Gimp. I'm trying to package my app as small as possible, a GIMP install is not an option for the EU.
Say you have 2 images, one is 400x500, other is 200x100. They both are white with a 100x100 textblock somewhere within each image's boundaries. What I'm trying to do is automatically strip the whitespace around that text, load that 100x100 image textblock into a variable for further text extraction.
It's obviously not this simple, so just running the text extraction on the whole image won't work! I just wanted to query about the basic process. There is not much available on Google about this topic. If solved, perhaps it could help someone else as well...
Thanks for reading!
If you put the image into a numpy array, it's simple to find the edges which you can use PIL to crop. Here I'm assuming that the whitespace is the color (255,255,255), you can adjust to your needs:
from PIL import Image
import numpy as np
im = Image.open("test.png")
pix = np.asarray(im)
pix = pix[:,:,0:3] # Drop the alpha channel
idx = np.where(pix-255)[0:2] # Drop the color when finding edges
box = map(min,idx)[::-1] + map(max,idx)[::-1]
region = im.crop(box)
region_pix = np.asarray(region)
To show what the results look like, I've left the axis labels on so you can see the size of the box region:
from pylab import *
subplot(121)
imshow(pix)
subplot(122)
imshow(region_pix)
show()
The general algorithmn would be to find the color of the top left pixel, and then do a spiral scan inwards until you find a pixel not of that color. That will define one edge of your bounding box. Keep scanning until you hit one more of each edge.
http://blog.damiles.com/2008/11/basic-ocr-in-opencv/
might be of some help. You can use the simple bounding box method described in that tutorial or #Tyler Eaves spiral suggestion which works equally as well

Categories