Split Image into arbitrary number of boxes - python

I need to split an RGBA image into an arbitrary number of boxes that are as equally sized as possible
I have attempted to use numpy.array_split, but am unsure of how to do so while preserving the RGBA channels
I have looked the following questions, none of them detail how to split an image into n boxes, they reference splitting the image into boxes of predetermined pixel size, or how to split the image into some shape.
While it seems that it would be some simple math to get number of boxes from box size and image size, I am unsure of how to do so.
How to Split Image Into Multiple Pieces in Python
Cutting one image into multiple images using the Python Image Library
Divide image into rectangles information in Python
While attempting to determine the number of boxes from pixel box size, I used the formula
num_boxes = (img_size[0]*img_size[1])/ (box_size_x * box_size_y)
but that did not result in the image being split up properly
To clarify, I would like to be able to input an image that is a numpy array of size (a,b,4) and a number of boxes and output the images in some form (np array preferred, but whatever works)
I appreciate any help, even if you aren't able to provide the full method, I would appreciate some direction.
I have tried
def split_image(image, n_boxes):
return numpy.array_split(image,n_boxes)
#doesn't work with colors
def split_image(image, n_boxes):
box_size = factor_int(n_boxes)
M = im.shape[0]//box_size[0]
N = im.shape[1]//box_size[1]
return [im[x:x+M,y:y+N] for x in range(0,im.shape[0],M) for y in range(0,im.shape[1],N)]
factor_int returns integer as close to a square as possible from Factor an integer to something as close to a square as possible

I am still not sure if your inputs are actually the image and the dimensions of the boxes or the image and the number of boxes. Nor am I sure if your problem is deciding where to chop the image or knowing how to chop a 4-channel image, but maybe something in here will get you started.
I started with this RGBA image - the circles are transparent, not white:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
import math
# Open image and get dimensions
im = Image.open('start.png').convert('RGBA')
# Make Numpy array from image and get height and width
ni = np.array(im)
h ,w = ni.shape[:2]
print(f'Height: {h}, width: {w}')
BOXES = 4
for i in range(BOXES):
this = ni[:, i*w//BOXES:(i+1)*w//BOXES, :]
Image.fromarray(this).save(f'box-{i}.png')
You can change BOXES but leaving it at 4 gets you these 4 output images:
[] []4

Related

Convert an image of a signal into a python list - by specifying no of points in 1D

I'm struggling to convert an image of a signal back to a python list (it was plotted a long time ago and I have lost the data I have only the images).
I've searched on the internet but I find answers about how to convert a 2d image into a 1d and I want to get the signal back.
Long story short:
I have this image of a signal:
and I want to convert this to a python list with a size of 65535 so my list should be looking like this:
list = [0.14, 0.144, 0.12 ...... ]
Thanks!
As a first plan, you could load the image using PIL/Pillow, or OpenCV, greyscale it and resize it to 65536 pixels wide by 100 pixels tall.
Then you will have a Numpy array with dimensions (100,65536). You can then run np.argmin() to find the index (y-value) of the darkest pixel in each column.
Or, find the indices of all the low valued pixels and take their median instead of the second step above.
The code starts off like this:
#!/usr/bin/env python3
from PIL import Image
import numpy as np
# Load image and convert to greyscale
im = Image.open('signal.png').convert('L')
# Resize to match required output
big = im.resize((65536,100), resample=Image.NEAREST)
# Make Numpy array
na = np.array(big)
# This looks about right, I think
print(np.argmin(na,axis=0))
If you trim the image so that the signal touches the edges all the way around, then the first black pixel on the left comes out as list element 0, the last pixel on the right comes out as the last element of your list and the lowest black pixel comes out with y-value of 0 and the peak comes out with y-value of 100.
Trimming would look like this:
from PIL import Image, ImageOps
import numpy as np
# Load image and convert to greyscale
im = Image.open('signal.png').convert('L')
# Get bounding box
bbox = ImageOps.invert(im).getbbox()
# Trim original image so that signal touches edge on all sides
im = im.crop(bbox)
... continue as before ...
Essentially, you'll have to "scan" the images left to right and identify the correct signal value at each "time step." As the image you presented doesn't have scale / units, you'll probably want to normalize all signal values from 0 to 1, as you've implied in your answer. Later you can adjust the scale of the signal if that's not the right range.
It looks like your images have some anti-aliasing at each step of the signal, which means that you won't have columns of all zeros except for one "signal" value. You'll have a cluster of signal values at each time step, some of which are weaker, because the image compression has blurred the signal slightly. This shouldn't be a problem, since you'll just find the max at each time step.
Assuming these images are in grayscale (if not, convert to grayscale), you'd want to find the maximum (or minimum, if the signal is drawn in black) color value at each column of pixels in the images (representing timesteps of the signal).
Mark Setchell's suggestion of PIL/Pillow seems like a great first step.
numpy's amax takes a matrix and flattens it to the max across an entire axis.

Images dimensions error in python

Trying to match two images to find out the scores between them.But it shows some dimension error.Unable to fix the issue.My code is given below:
from skimage.measure import compare_ssim
#import argparse
#import imutils
import cv2
img1="1.png"
img2="2.png"
# load the two input images
imageA = cv2.imread(img1)
imageB = cv2.imread(img2)
# convert the images to grayscale
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
# compute the Structural Similarity Index (SSIM) between the two
# images, ensuring that the difference image is returned
(score, diff) = compare_ssim(grayA, grayB, full=True)
diff = (diff * 255).astype("uint8")
print("SSIM: {}".format(score))
This give n an error:
raise ValueError('Input images must have the same dimensions.')
ValueError: Input images must have the same dimensions.
How to fix this issue?
Amending Saurav Panda's answer:
You can reshape one of the images to the size of other image like this:
imageB=cv2.resize(imageB,imageA.shape)
note that
(H, W) = imageA.shape
# to resize and set the new width and height
imageB = cv2.resize(imageB, (W, H))
the cv2.resize function inputs expects (W,H). This is the reverse order of the output of cv2.shape (H,W), so you need to catch that, or you'll get the same error when comparing non-square images.
You can do this in many ways:
Like in the first method, you can assign a fixed dimension which would be less than the actual dimensions of the image and resize both images to this same size. Like, resize all images to (150,150), etc.
In second method you can reshape one of the images to the size of other images.
Try this code:
imageB=cv2.resize(imageB,imageA.shape)
This will work for you, but in case the difference in dimensions of two image is very large, sometimes you may lose some data. You can compare for both x and y dimensions and find the smallest one.Then resize both images to this smallest dimension of x and y.
The error
'Input images must have the same dimensions.'
Tells you that the function you called expects input images of the same dimensions and that you did not do this.
You obviously fix that by providing input images that have the same dimensions or by not calling that function if the images have different dimensions and if you cannot change that for whatever reason.
Compare imageA.shape and imageB.shape after loading the images from file.
For simple debugging:
print imageA.shape
print imageB.shape
You can use tensorflow. See this link and you can modify your data accordingly

How to get border pixels of an image in python?

I have an image, using steganography I want to save the data in border pixels only.
In other words, I want to save data only in the least significant bits(LSB) of border pixels of an image.
Is there any way to get border pixels to store data( max 15 characters text) in the border pixels?
Plz, help me out...
OBTAINING BORDER PIXELS:
Masking operations are one of many ways to obtain the border pixels of an image. The code would be as follows:
a= cv2.imread('cal1.jpg')
bw = 20 //width of border required
mask = np.ones(a.shape[:2], dtype = "uint8")
cv2.rectangle(mask, (bw,bw),(a.shape[1]-bw,a.shape[0]-bw), 0, -1)
output = cv2.bitwise_and(a, a, mask = mask)
cv2.imshow('out', output)
cv2.waitKey(5000)
After I get an array of ones with the same dimension as the input image, I use cv2.rectangle function to draw a rectangle of zeros. The first argument is the image you want to draw on, second argument is start (x,y) point and the third argument is the end (x,y) point. Fourth argument is the color and '-1' represents the thickness of rectangle drawn (-1 fills the rectangle). You can find the documentation for the function here.
Now that we have our mask, you can use 'cv2.bitwise_and' (documentation) function to perform AND operation on the pixels. Basically what happens is, the pixels that are AND with '1' pixels in the mask, retain their pixel values. Pixels that are AND with '0' pixels in the mask are made 0. This way you will have the output as follows:
.
The input image was :
You have the border pixels now!
Using LSB planes to store your info is not a good idea. It makes sense when you think about it. A simple lossy compression would affect most of your hidden data. Saving your image as JPEG would result in loss of info or severe affected info. If you want to still try LSB, look into bit-plane slicing. Through bit-plane slicing, you basically obtain bit planes (from MSB to LSB) of the image. (image from researchgate.net)
I have done it in Matlab and not quite sure about doing it in python. In Matlab,
the function, 'bitget(image, 1)', returns the LSB of the image. I found a question on bit-plane slicing using python here. Though unanswered, you might want to look into the posted code.
To access border pixel and enter data into it.
A shape of an image is accessed by t= img.shape. It returns a tuple of the number of rows, columns, and channels.A component is RGB which 1,2,3 respectively.int(r[0]) is variable in which a value is stored.
import cv2
img = cv2.imread('xyz.png')
t = img.shape
print(t)
component = 2
img.itemset((0,0,component),int(r[0]))
img.itemset((0,t[1]-1,component),int(r[1]))
img.itemset((t[0]-1,0,component),int(r[2]))
img.itemset((t[0]-1,t[1]-1,component),int(r[3]))
print(img.item(0,0,component))
print(img.item(0,t[1]-1,component))
print(img.item(t[0]-1,0,component))
print(img.item(t[0]-1,t[1]-1,component))
cv2.imwrite('output.png',img)

Python - matplotlib - imshow - How to influence displayed value of unzoomed image

I need to search outliers in more or less homogeneous images representing some physical array. The images have a resolution which is much higher than the screen resolution. Thus every pixel on screen originates from a block of image pixels. Is there the possibility to customize the algorithm which calculates the displayed value for such a block? Especially the possibility to either use the lowest or the highest value would be helpful.
Thanks in advance
Scipy provides several such filters. To get a new image (new) whose pixels are the maximum/minimum over a w*w block of an original image (img), you can use:
new = scipy.ndimage.filters.maximum_filter(img, w)
new = scipy.ndimage.filters.minimum_filter(img, w)
scipy.ndimage.filters has several other filters available.
If the standard filters don't fit your requirements, you can roll your own. To get you started here is an example that shows how to get the minimum in each block in the image. This function reduces the size of the full image (img) by a factor of w in each direction. It returns a smaller image (new) in which each pixel is the minimum pixel in a w*w block of pixels from the original image. The function assumes the image is in a numpy array:
import numpy as np
def condense(img, w):
new = np.zeros((img.shape[0]/w, img.shape[1]/w))
for i in range(0, img.shape[1]//w):
col1 = i * w
new[:, i] = img[:, col1:col1+w].reshape(-1, w*w).min(1)
return new
If you wanted the maximum, replace min with max.
For the condense function to work well, the size of the full image must be a multiple of w in each direction. The handling of non-square blocks or images that don't divide exactly is left as an exercise for the reader.

PIL: scale image while maintaing highest possible quality

I'm using PIL to scale images that range anywhere from 600px wide to 2400px wide down to around 200px wide. I've already incorporated Image.ANTIALIAS and set quality=95 to try and get the highest quality image possible.
However the scaled down images still have pretty poor quality compared to the originals.
Here's the code that I'm using:
# Open the original image
fp = urllib.urlopen(image_path)
img = cStringIO.StringIO(fp.read())
im = Image.open(img)
im = im.convert('RGB')
# Resize the image
resized_image = ImageOps.fit(im, size, Image.ANTIALIAS)
# Save the image
resized_image_object = cStringIO.StringIO()
resized_image.save(resized_image_object, image_type, quality=95)
What's the best way to scale an image along these ratios while preserving as much of the image quality as possible?
I should note that my primary goal is get the maximum quality image possible. I'm not really concerned with how efficient the process is time wise.
If you can't get results with the native resize options in PIL, you can manually calculate the resize pixel values by running them through your own resizing function. There are three main algorithms (that I know of) for resizing images:
Nearest Neighbor
Bilinear Interpolation
Bicubic Interpolation
The last one will produce the highest quality image at the longest calculation time. To do this, imagine the pixel layout of the the smaller image, then scale it up to match the larger image and think about where the new pixel locations would be over the old ones. Then for each new pixel take the average value of the 16 nearest pixels (4x4 radius around it) and use that as its new value.
The resulting values for each of the pixels in the small image will be a smooth but clear resized version of the large image.
For further reading look here: Wikipedia - Bicubic interpolation
Try a different approach. I'm not sure if this will help, but I did something similar a while back:
https://stackoverflow.com/a/13211834/1339024
It may be that the original image on the urlpath is not that great quality to begin with. But if you want, try my script. I made it to shrink images in a given directory, but this portion could be of use:
parentDir = "Some\\Path"
width = 200
height = 200
cdpi = 75
cquality = 95
a = Image.open(parentDir+'\\'+imgfile) # Change this to your url type
iw,ih = a.size
if iw > width or ih > height:
pcw = width/float(iw)
pch = height/float(ih)
if pcw <= pch:
LPC = pcw
else:
LPC = pch
if 'gif' in imgfile:
a = a.convert("RGB")#,dither=Image.NONE)
a = a.resize((int(iw*LPC),int(ih*LPC)),Image.ANTIALIAS)
a = a.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE)
a.save(outputDir+"\\"+imgfile,dpi=(cdpi,cdpi), quality=cquality)
else:
a = a.resize((int(iw*LPC),int(ih*LPC)),Image.ANTIALIAS)
a.save(outputDir+"\\"+imgfile,dpi=(cdpi,cdpi), quality=cquality)

Categories