Sorting and editing a Dictionary in Python - python

I use the CCL algorithm of Spencer Whitt from Github to remove detected road markings from Noise.
First, I run the CCL algorithm over an image so that it recognizes contiguous elements. Now I extended the algorithm so that it ignores elements with a small number of pixels. So I tried to sort and filter the dictionary created by the CCL algorithm. In the additional code, therefore, keys that are not enough are deleted with the corresponding values.
Unfortunately, the additional code not only deletes small elements, but also fragments up the "large" contiguous road markings. Why?
Image:
CCL algorithm:
from PIL import Image
import PIL
from PIL import ImageOps
import collections
import operator
import numpy as np
import sys
import math, random
from itertools import product
from ufarray import *
Image.MAX_IMAGE_PIXELS = 1000000000
import cv2
def run(img):
data = img.load()
width, height = img.size
# Union find data structure
uf = UFarray()
#
# First pass
#
# Dictionary of point:label pairs
labels = {}
for y, x in product(range(height), range(width)):
#
# Pixel names were chosen as shown:
#
# -------------
# | a | b | c |
# -------------
# | d | e | |
# -------------
# | | | |
# -------------
#
# The current pixel is e
# a, b, c, and d are its neighbors of interest
#
# 255 is white, 0 is black
# White pixels part of the background, so they are ignored
# If a pixel lies outside the bounds of the image, it default to white
#
# If the current pixel is white, it's obviously not a component...
if data[x, y] == 255:
pass
# If pixel b is in the image and black:
# a, d, and c are its neighbors, so they are all part of the same component
# Therefore, there is no reason to check their labels
# so simply assign b's label to e
elif y > 0 and data[x, y-1] == 0:
labels[x, y] = labels[(x, y-1)]
# If pixel c is in the image and black:
# b is its neighbor, but a and d are not
# Therefore, we must check a and d's labels
elif x+1 < width and y > 0 and data[x+1, y-1] == 0:
c = labels[(x+1, y-1)]
labels[x, y] = c
# If pixel a is in the image and black:
# Then a and c are connected through e
# Therefore, we must union their sets
if x > 0 and data[x-1, y-1] == 0:
a = labels[(x-1, y-1)]
uf.union(c, a)
# If pixel d is in the image and black:
# Then d and c are connected through e
# Therefore we must union their sets
elif x > 0 and data[x-1, y] == 0:
d = labels[(x-1, y)]
uf.union(c, d)
# If pixel a is in the image and black:
# We already know b and c are white
# d is a's neighbor, so they already have the same label
# So simply assign a's label to e
elif x > 0 and y > 0 and data[x-1, y-1] == 0:
labels[x, y] = labels[(x-1, y-1)]
# If pixel d is in the image and black
# We already know a, b, and c are white
# so simpy assign d's label to e
elif x > 0 and data[x-1, y] == 0:
labels[x, y] = labels[(x-1, y)]
# All the neighboring pixels are white,
# Therefore the current pixel is a new component
else:
labels[x, y] = uf.makeLabel()
#
# Second pass
#
uf.flatten()
colors = {}
# Image to display the components in a nice, colorful way
output_img = Image.new("RGB", (width, height))
outdata = output_img.load()
for (x, y) in labels:
# Name of the component the current point belongs to
component = uf.find(labels[(x, y)])
# Update the labels with correct information
labels[(x, y)] = component
# Associate a random color with this component
if component not in colors:
colors[component] = (random.randint(0,255), random.randint(0,255),random.randint(0,255))
# Colorize the image
outdata[x, y] = colors[component]
return (labels, output_img)
Additional code:
def main():
# Open the image
img = Image.open("files/motorway/gabor/motorway_gabor_S.tiff")
img = PIL.ImageOps.invert(img)
# Threshold the image, this implementation is designed to process b+w
# images only
img = img.point(lambda p: p > 190 and 255)
img = img.convert('1')
# labels is a dictionary of the connected component data in the form:
# (x_coordinate, y_coordinate) : component_id
#
# if you plan on processing the component data, this is probably what you
# will want to use
#
# output_image is just a frivolous way to visualize the components.
(labels, output_img) = run(img)
# output_img.show()
output_img.save("files/motorway/gabor/motorway_gabor_S_cc1.tiff")
################################################################################
sorted_x = sorted(labels.items(), key=operator.itemgetter(1))
str_labels = str(sorted_x) # Convert Dictionary to String, remove unnecessary characters
str_labels = str_labels.replace("{", "[")
str_labels = str_labels.replace("}", "]")
str_labels = str_labels.replace("(", "")
str_labels = str_labels.replace(")", "")
str_labels = str_labels.replace(":", ",")
# print str_labels[:100]
int_labels = eval(str_labels) # Transforming back into an integer list
i = 0
key = []
value = []
#int_labels2 = list(int_labels)
while i < len(int_labels)/3:
key.append(int_labels[3*i]) # x (keys)
key.append(int_labels[3 * i + 1]) # y (keys)
value.append(int_labels[3 * i + 2]) # values
i = i + 1
counter = collections.Counter(value) # Counting the frequency of "values"
counter_values = counter.values()
################################################################################
# Selection
# Sorting out "keys" and "values" that are too big/small
# key2 = relevant "keys"
# value2 = to key2 appropriate relevant values
i = 0
k = 0
v = 0
key2 = []
key3 = []
value2 = []
value3 = []
help = []
while i < len(counter_values):
if counter_values[i] >= 80:# and counter_values[i] <= 71:
value2.append(counter_values[v]*2)
v = v + 1
while k < counter_values[i]*2 + sum(help)*2:
key2.append(key[k])
k = k + 1
else:
value3.append(counter_values[v]*2)
v = v + 1
while k < counter_values[i]*2 + sum(help)*2:
key3.append(key[k])
k = k + 1
help.append(counter_values[i])
i = i + 1
#####################################################################
# Image to display the components in a nice, colorful way
width, height = img.size
output_img = Image.new("RGB", (width, height))
outdata = output_img.load()
# list (key2) to x- and y-tuple-pairs
i = 0
key2t = []
while i < len(key2):
key2t.append(tuple(key2[i:i + 2]))
i = i + 2
# transform value2-list
i = 0
j = 0
k = 0
value2_full = []
while i < len(value2):
while j < value2[i]:
value2_full.append(k)
j = j + 1
j = 0
i = i + 1
k = k + 1
d = dict(zip(key2t, value2_full))
colors = {}
i = 0
for (x, y) in d:
if value2_full[i] not in colors:
colors[value2_full[i]] = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
# Colorize the image
outdata[x, y] = colors[value2_full[i]]
output_img.save("files/motorway/gabor/motorway_gabor_S_cc2.tiff")

Related

How to write points coordintates not index?

I have a code that takes set of points of a point cloud and detects multiple planes. the code gives me the equation of planes which have points more than a certain number (here 4000) belonging to it. All I need is to write the coordinates of points (not their index) belonging to these planes in a text file.
The code:
def ReadPlyPoint(fname):
pcd = o3d.io.read_point_cloud(fname)
return PCDToNumpy(pcd)
def NumpyToPCD(xyz):
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(xyz)
return pcd
def PCDToNumpy(pcd):
return np.asarray(pcd.points)
def PlaneRegression(points, threshold=0.01, init_n=3, iter=1000):
pcd = NumpyToPCD(points)
w, index = pcd.segment_plane(threshold, init_n, iter)
[a, b, c, d] = w
if len(index) > 4000 :
print (f'plane equation: {a:.2f}x + {b:.2f}y + {c:.2f}z + {d:.2f} = 0')
return w, index
def DetectMultiPlanes(points, min_ratio=0.05, threshold=0.01, iterations=1000):
plane_list = []
N = len(points)
target = points.copy()
count = 0
while count < (1 - min_ratio) * N:
w, index = PlaneRegression(target, threshold=threshold, init_n=3, iter=iterations)
count += len(index)
plane_list.append((w, target[index]))
target = np.delete(target, index, axis=0)
return plane_list
if __name__ == "__main__":
import random
points = ReadPlyPoint('segdown.ply')
results = DetectMultiPlanes(points, min_ratio=0.05, threshold=0.01, iterations=3000)
planes = []
colors = []
for _, plane in results:
r = random.random()
g = random.random()
b = random.random()
color = np.zeros((plane.shape[0], plane.shape[1]))
color[:, 0] = r
color[:, 1] = g
color[:, 2] = b
planes.append(plane)
colors.append(color)
planes = np.concatenate(planes, axis=0)
colors = np.concatenate(colors, axis=0)
DrawResult(planes, colors)
I have tried this but the points stored don't belong to the detected planes :
def PlaneRegression(points, threshold=0.01, init_n=3, iter=1000):
f = open('primitives_only1.txt', 'a')
pcd = NumpyToPCD(points)
w, index = pcd.segment_plane(threshold, init_n, iter)
[a, b, c, d] = w
if len(index) > 4000 :
for i in range(len(index)):
f.write("{} \n".format(str(points[i])))
f.close()
return w, index
detected planes

box stacking algorithm , each box used once

Given a group of boxes. I want arrange the boxes on top of each other to reach the maximum height. box cannot be placed on top of another box unless the area of 2D base is <= the 2D base of the lower box. allowed to rotated any box to use any two sides as its base.
For example, consider below 4 boxes where each box has the following dimensions
Input: Box 1: (4,5,2), Box 2:(3,1,6), Box 3:(3,2,1), Box 4:(6,8,3)
Output: From bottom to top as follows:
Box 4 on the base (6,3) and height 8,
then Box 1 on the base (4,2) and height 5,
then Box 2 on the base (1,3) and height 6,
finally, on top Box 3 on the base (2,1) and height 3.
The total height is 22
this solution is work but use all instances of all boxes but i need use only one instance for each box.
this code get the same height but use the box 2 twice and ignore another box
the problem is in if statement in this 2 for loop
> for i in range(1, n):
for j in range(0, i):
if (rot[i].w <= rot[j].w and rot[i].l <= rot[j].l) and rot[i].boxNo != rot[j].boxNo:
if msh[i] < msh[j] + rot[i].h:
msh[i] = msh[j] + rot[i].h
how to prevent using the same box twice?
class Box:
def __init__(self,l, w, h):
self.h = h
self.w = w
self.l = l
self.boxNo = 0
def __lt__(self,other):
return self.l * self.w < other.l * other.w
def maxStackHeight(arr, n):
# Create an array of all rotations of given boxes.
rot = [Box(0, 0, 0) for _ in range(3 * n)]
index = 0
no=1
for i in range(n):
# original box
rot[index].h = arr[i].h
rot[index].l = max(arr[i].l, arr[i].w)
rot[index].w = min(arr[i].l, arr[i].w)
rot[index].boxNo=no
index = index+ 1
# First rotation
rot[index].h = arr[i].w
rot[index].l = max(arr[i].h, arr[i].l)
rot[index].w = min(arr[i].h, arr[i].l)
rot[index].boxNo = no
index = index + 1
# Second rotation
rot[index].h = arr[i].l
rot[index].l = max(arr[i].h, arr[i].w)
rot[index].w = min(arr[i].h, arr[i].w)
rot[index].boxNo = no
index = index + 1
no=no+1
n=n*3 # new number of boxes
rot.sort(reverse=True) #Sort array in descending order of base area
msh = [0] * n
for i in range(n):
msh[i] = rot[i].h
# Compute optimized msh values in bottom up manner
for i in range(1, n):
for j in range(0, i):
if (rot[i].w <= rot[j].w and rot[i].l <= rot[j].l) and rot[i].boxNo != rot[j].boxNo:
if msh[i] < msh[j] + rot[i].h:
msh[i] = msh[j] + rot[i].h
maxm = -1
for i in range(n):
maxm = max(maxm, msh[i])
return maxm
arr = [Box(4,5,2),Box(3,1,6),Box(3,2,1),Box(6,8,3)]
n = len(arr)
print("The maximum possible height of stack is",maxStackHeight(arr, n))
This ought to do the trick, it uses itertools module to create every possible combination of the boxes using cartesian product, and only checks ones that meet the criteria for your problem.
from itertools import product
class Box:
def __init__(self,l, w, h):
self.h = h
self.w = w
self.l = l
self.boxNo = 0
def __lt__(self,other):
return self.l * self.w < other.l * other.w
def maxStackHeight(arr, n):
# Create an array of all rotations of given boxes.
rot = [Box(0, 0, 0) for _ in range(3 * n)]
numBoxes = n
index = 0
no=1
for i in range(n):
# original box
rot[index].h = arr[i].h
rot[index].l = max(arr[i].l, arr[i].w)
rot[index].w = min(arr[i].l, arr[i].w)
rot[index].boxNo=no
index = index+ 1
# First rotation
rot[index].h = arr[i].w
rot[index].l = max(arr[i].h, arr[i].l)
rot[index].w = min(arr[i].h, arr[i].l)
rot[index].boxNo = no
index = index + 1
# Second rotation
rot[index].h = arr[i].l
rot[index].l = max(arr[i].h, arr[i].w)
rot[index].w = min(arr[i].h, arr[i].w)
rot[index].boxNo = no
index = index + 1
no=no+1
rot.sort(reverse=True) #Sort array in descending order of base area
# Compute optimized msh values in bottom up manner
workable = []
for combo in product(rot, repeat = numBoxes):
if len(set(b.boxNo for b in combo)) != numBoxes:
continue
canwork = True
for a, b in zip(combo[:-1], combo[1:]):
if a < b :
canwork = False
if canwork:
workable.append(combo)
return max(sum(box.h for box in combo) for combo in workable)
arr = [Box(4,5,2),Box(3,1,6),Box(3,2,1),Box(6,8,3)]
n = len(arr)
print("The maximum possible height of stack is",maxStackHeight(arr, n))

Script freezes after completing a 'while' loop in a 'while' loop (oops)

How can get the RGB values of every pixel in an image and after it gets all the values of the first row?
Script:
image = input("image:")
im = Image.open(image)
pix = im.load()
width, height = im.size
x = 0
y = 0
# For each pixel in the Y
while (y < height):
# For each pixel in the X
while (x < width):
print pix[x,y]
x = x + 1
y = y + 1
The way you initialize your x and y values is the problem. X should be initialized back to zero immediately before the second while loop, so that the count starts again for the width of the next row.
Something like:
x = 0
y = 0
#for each pixel in the Y
while (y < height):
# for each pixel in the X
x = 0 #start counting again for the next row
while (x < width):
print pix[x,y]
x = x + 1
y = y + 1
Your loop freezes, because at the end of the first row, x=width and you forget to reset it back to zero for the second iteration of the first while loop.

Finding the union of multiple overlapping rectangles - OpenCV python

I have several overlapping bounding boxes that encompass a single object, however they overlap minimally in some places. Taken as a whole, they encompass the entire object, but openCV's groupRectangles function does not return a box encompassing the object. The bounding boxes I have are shown in blue, and bounding boxes I would like to return are shown in red here
I would like to get the union of only the overlapping rectangles but am unsure about how to iterate through the list without combining every rectangle.
I have union and intersect functions shown below, and a list of the rectangles represented by (x y w h), where x and y are the coordinates of the top left corner of the box.
def union(a,b):
x = min(a[0], b[0])
y = min(a[1], b[1])
w = max(a[0]+a[2], b[0]+b[2]) - x
h = max(a[1]+a[3], b[1]+b[3]) - y
return (x, y, w, h)
def intersection(a,b):
x = max(a[0], b[0])
y = max(a[1], b[1])
w = min(a[0]+a[2], b[0]+b[2]) - x
h = min(a[1]+a[3], b[1]+b[3]) - y
if w<0 or h<0: return () # or (0,0,0,0) ?
return (x, y, w, h)
My function for combining is currently as follows:
def combine_boxes(boxes):
noIntersect = False
while noIntersect == False and len(boxes) > 1:
a = boxes[0]
print a
listBoxes = boxes[1:]
print listBoxes
index = 0
for b in listBoxes:
if intersection(a, b):
newBox = union(a,b)
listBoxes[index] = newBox
boxes = listBoxes
noIntersect = False
index = index + 1
break
noIntersect = True
index = index + 1
print boxes
return boxes.astype("int")
This gets most of the way there, as shown here
there are still a few nested bounding boxes that I'm not sure how to continue iterating through.
I haven't worked with openCV, so the object may need more mangling, but maybe use itertools.combinations to make the combine_boxes function simpler:
import itertools
import numpy as np
def combine_boxes(boxes):
new_array = []
for boxa, boxb in itertools.combinations(boxes, 2):
if intersection(boxa, boxb):
new_array.append(union(boxa, boxb))
else:
new_array.append(boxa)
return np.array(new_array).astype('int')
EDIT (you may actually need zip instead)
for boxa, boxb in zip(boxes, boxes[1:])
everything is the same.
Thank you, salparadise (https://stackoverflow.com/users/62138/salparadise). Very helpful to find a way out.
But the solution looks rectangles could be repeated added into the new_array. e.g. A B C has no intersection to each other, A B C will be added twice respectively. So the new_array will contain A B A C B C.
Please refer to the revised code. Hope it helps.
Had tested it on multiple test cases. It looks working fine.
def merge_recs(rects):
while (1):
found = 0
for ra, rb in itertools.combinations(rects, 2):
if intersection(ra, rb):
if ra in rects:
rects.remove(ra)
if rb in rects:
rects.remove(rb)
rects.append((union(ra, rb)))
found = 1
break
if found == 0:
break
return rects
I go into a similar situation to combine all the intersected rectangle found in each frame of my OpenCV project, after some time I finally come up with a solution and want to share it here for someone having a headache combining those rectangles. (This might not be the best solution but it's simple though)
import itertools
# my Rectangle = (x1, y1, x2, y2), a bit different from OP's x, y, w, h
def intersection(rectA, rectB): # check if rect A & B intersect
a, b = rectA, rectB
startX = max( min(a[0], a[2]), min(b[0], b[2]) )
startY = max( min(a[1], a[3]), min(b[1], b[3]) )
endX = min( max(a[0], a[2]), max(b[0], b[2]) )
endY = min( max(a[1], a[3]), max(b[1], b[3]) )
if startX < endX and startY < endY:
return True
else:
return False
def combineRect(rectA, rectB): # create bounding box for rect A & B
a, b = rectA, rectB
startX = min( a[0], b[0] )
startY = min( a[1], b[1] )
endX = max( a[2], b[2] )
endY = max( a[3], b[3] )
return (startX, startY, endX, endY)
def checkIntersectAndCombine(rects):
if rects is None:
return None
mainRects = rects
noIntersect = False
while noIntersect == False and len(mainRects) > 1:
mainRects = list(set(mainRects))
# get the unique list of rect, or the noIntersect will be
# always true if there are same rect in mainRects
newRectsArray = []
for rectA, rectB in itertools.combinations(mainRects, 2):
newRect = []
if intersection(rectA, rectB):
newRect = combineRect(rectA, rectB)
newRectsArray.append(newRect)
noIntersect = False
# delete the used rect from mainRects
if rectA in mainRects:
mainRects.remove(rectA)
if rectB in mainRects:
mainRects.remove(rectB)
if len(newRectsArray) == 0:
# if no newRect is created = no rect in mainRect intersect
noIntersect = True
else:
# loop again the combined rect and those remaining rect in mainRects
mainRects = mainRects + newRectsArray
return mainRects
It's horribly janky, but after a bit of finagling I did manage to get the results I wanted
I have included my combine_boxes function below in case anyone is having a similar problem.
def combine_boxes(boxes):
noIntersectLoop = False
noIntersectMain = False
posIndex = 0
# keep looping until we have completed a full pass over each rectangle
# and checked it does not overlap with any other rectangle
while noIntersectMain == False:
noIntersectMain = True
posIndex = 0
# start with the first rectangle in the list, once the first
# rectangle has been unioned with every other rectangle,
# repeat for the second until done
while posIndex < len(boxes):
noIntersectLoop = False
while noIntersectLoop == False and len(boxes) > 1:
a = boxes[posIndex]
listBoxes = np.delete(boxes, posIndex, 0)
index = 0
for b in listBoxes:
#if there is an intersection, the boxes overlap
if intersection(a, b):
newBox = union(a,b)
listBoxes[index] = newBox
boxes = listBoxes
noIntersectLoop = False
noIntersectMain = False
index = index + 1
break
noIntersectLoop = True
index = index + 1
posIndex = posIndex + 1
return boxes.astype("int")
The most voted answer will not work if you need a single maximum box, however the above one will work, but it has a bug.
posting the correct code for someone
tImageZone = namedtuple('tImageZone', 'x y w h')
def merge_zone(z1, z2):
if (z1.x == z2.x and z1.y == z2.y and z1.w == z2.w and z1.h == z2.h):
return z1
x = min(z1.x, z2.x)
y = min(z1.y, z2.y)
w = max(z1.x + z1.w, z2.x + z2.w) - x
h = max(z1.y + z1.h, z2.y + z2.h) - y
return tImageZone(x, y, w, h)
def is_zone_overlap(z1, z2):
# If one rectangle is on left side of other
if (z1.x > z2.x + z2.w or z1.x + z1.w < z2.x):
return False
# If one rectangle is above other
if (z1.y > z2.y + z2.h or z1.y + z1.h < z2.y):
return False
return True
def combine_zones(zones):
index = 0
if zones is None: return zones
while index < len(zones):
no_Over_Lap = False
while no_Over_Lap == False and len(zones) > 1 and index < len(zones):
zone1 = zones[index]
tmpZones = np.delete(zones, index, 0)
tmpZones = [tImageZone(*a) for a in tmpZones]
for i in range(0, len(tmpZones)):
zone2 = tmpZones[i]
if (is_zone_overlap(zone1, zone2)):
tmpZones[i] = merge_zone(zone1, zone2)
zones = tmpZones
no_Over_Lap = False
break
no_Over_Lap = True
index += 1
return zones

Python average color over square region

I'm using four inputs for my function: a picture object, x coordinate, y coordinate, and the height/width of the square. I want to separately average all the red, green, and blue values of the picture. I'm having trouble with the accumulator variables of the RGB pixels and checking the bounds of the square. Can anyone help me out?
from imageTools import *
p1 = makePicture("flower.jpg")
def averageColor(pic, xCord, yCord, width):
rAcc = 0
gAcc = 0
bAcc = 0
for x in range(xCord, xCord + width):
for y in range(yCord, yCord + width):
picWidth = getWidth(pic)
picHeight = getHeight(pic)
if x <= picWidth and y <= picHeight:
pixel = getPixel(pic, xCord, yCord)
r = getRed(pixel)
g = getGreen(pixel)
b = getBlue(pixel)
rAcc = rAcc + 1
gAcc = gAcc + 1
bAcc = bAcc + 1
avgRed = r / rAcc
avgGreen = g / gAcc
avgBlue = b / bAcc
newColor = makeColor(avgRed, avgGreen, avgBlue)
return newColor
col1 = averageColor(p1, 0, 150, 100)
print(col1)
You want to use < and not <= when comparing indexes because indexes are zero based. For instance if the picture is 10 pixels wide 9 is the index to the last pixel
Also you are overwriting your accumulation variables r, g and b. You need to do
r = r + getRed(...)
oh. Also it looks like you are trying to use xCoord and yCoord to get the pixel rather than your x and y variables

Categories

Resources