Related
Anyone here familiar with BFS and tracking? I have written the algorithm for the first time and it finds the shortest path, but tracing the shortest path is the part i'm stuck at. The list below is a list of previous indexes (y, x), and when it reaches (0, 5) there are two paths, one path to the left and another to the right, i only want to include the path that leads to the destination, however i have no clue how to make it work. I keep track of previous node, but once we get to (0, 5) the setting of previous starts messing up, because there are two paths. Because of this i can't backtrace from the destination.
How have you kept track of previous and made it work? I have read so many articles but still havent found anything that really explains it to me.
Any help is greatly appreciated
[
(0, 0),
(1, 0),
(2, 0),
(2, 1),
(2, 2),
(2, 3),
(3, 3),
(4, 3),
(5, 3),
(5, 4),
(5, 5),
(4, 5),
(3, 5),
(2, 5),
(1, 5),
(0, 5), <-- starts to mess up here becuase there are two paths to take, left and right
(0, 6), <-- to the right
(0, 4), <-- to the left
(0, 7), <-- to the right
(0, 3), <-- to the left
(0, 8), <-- to the right
(1, 7), <-- to the left and one down
(0, 2), <-- to the left
(0, 9) <-- to the right (also happens to be the destination)
]
Code:
use std::collections::VecDeque;
fn bfs(arr: [[i32; 10]; 10], target: i32) -> bool {
let mut visited = [[false, false, false, false, false, false, false, false, false, false]; 10];
let mut queue: VecDeque<(i32, i32)> = VecDeque::new();
queue.push_back((0, 0));
let mut previous_nodes = Vec::new();
let mut previous = (-1, -1);
while !queue.is_empty() {
let (y, x) = queue.pop_front().unwrap();
if visited[y as usize][x as usize] == true {
continue;
}
visited[y as usize][x as usize] = true;
previous_nodes.push(previous);
previous = (y, x);
print!("{}[2J", 27 as char);
for y in 0..visited.len() {
for x in 0..visited.len() {
if arr[y][x] == target && visited[y][x] == true {
print!("X ");
} else if visited[y][x] == true {
print!("0 ");
} else if arr[y][x] == 3 {
print!("# ");
} else {
print!(". ");
}
}
print!("\n");
}
print!("\n");
if arr[y as usize][x as usize] == target {
for entry in previous_nodes {
println!("{:?}", entry);
}
return true;
}
if x + 1 < arr.len() as i32 && arr[y as usize][(x + 1) as usize] != 3 {
queue.push_back((y, x + 1));
}
if y + 1 < arr.len() as i32 && arr[(y + 1) as usize][x as usize] != 3 {
queue.push_back((y + 1, x));
}
if x - 1 >= 0 && arr[y as usize][(x - 1) as usize] != 3 {
queue.push_back((y, x - 1));
}
if y - 1 >= 0 && arr[(y - 1) as usize][x as usize] != 3 {
queue.push_back((y - 1, x));
}
}
false
}
fn main() {
let data = [
[0, 3, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 3, 3, 3, 3, 0, 3, 0, 3, 3],
[0, 0, 0, 0, 3, 0, 3, 0, 0, 0],
[3, 3, 3, 0, 3, 0, 3, 3, 3, 0],
[0, 0, 3, 0, 3, 0, 3, 0, 3, 0],
[0, 0, 3, 0, 0, 0, 3, 0, 3, 0],
[0, 3, 3, 3, 3, 3, 3, 0, 3, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];
let b = bfs(data, 1);
println!("Found: {}", b);
}
First off, I started with a little bit of code cleanup to make it a bit more concise and made a rust playground to experiment with. There are two main approaches to solving this problem. Either you keep track of the path taken in the queue or the visited nodes. The easiest approach for you would likely be to simply adapt your code to have the visited nodes array point to the previous node in the path from each visited node. playground link
fn bfs(arr: [[i32; 10]; 10], target: i32) -> Option<Vec<(i32, i32)>> {
let mut visited = [[None; 10]; 10];
let mut queue: VecDeque<(i32, i32)> = VecDeque::new();
queue.push_back((0, 0));
// Put some filler into the first location
visited[0][0] = Some((0, 0));
while let Some((y, x)) = queue.pop_front() {
// Prind debug info
println!("\nExpanding ({}, {})", x, y);
for y in 0..visited.len() {
for x in 0..visited.len() {
if arr[y][x] == target && visited[y][x].is_some() {
print!("X ");
} else if visited[y][x].is_some() {
print!("0 ");
} else if arr[y][x] == 3 {
print!("# ");
} else {
print!(". ");
}
}
println!();
}
// Check if this position is the target
if arr[y as usize][x as usize] == target {
let mut path_taken = Vec::new();
path_taken.push((y, x));
let mut prev_x = x;
let mut prev_y = y;
while prev_x != 0 || prev_y != 0 {
let (py, px) = visited[prev_y as usize][prev_x as usize].unwrap();
path_taken.push((py, px));
prev_y = py;
prev_x = px;
}
return Some(path_taken.into_iter().rev().collect())
}
// Iterate over adjacent offsets
for (dx, dy) in &[(1, 0), (0, 1), (-1, 0), (0, -1)] {
// Check if offset is within bounds
if x + dx < 0
|| y + dy < 0
|| (y + dy) as usize >= arr.len()
|| (x + dx) as usize >= arr[(y + dy) as usize].len()
{
continue;
}
// Check if offset points to valid location
if arr[(y + dy) as usize][(x + dx) as usize] == 3 {
continue;
}
if visited[(y + dy) as usize][(x + dx) as usize].is_some() {
continue;
}
visited[(y + dy) as usize][(x + dx) as usize] = Some((y, x));
queue.push_back((y + dy, x + dx));
}
}
None
}
However, I personally prefer the approach of keeping track of the path taken in the queue to reduce the memory requirement for small paths. While it is not as true to your original question, my favorite version of this would be to write BFS in a way that better represents how it described mathematically using type parameters. playground link
fn bfs<N, F, R>(start: N, end: N, expand: F) -> Option<SearchPath<N>>
where N: Copy + Eq + Hash,
F: Fn(N) -> R,
R: IntoIterator<Item=N> {
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(SearchPath(start, None));
visited.insert(start);
while let Some(SearchPath(node, path)) = queue.pop_front() {
if node == end {
return Some(SearchPath(node, path))
}
let path = Rc::new(SearchPath(node, path.clone()));
for edge in expand(node) {
if !visited.contains(&edge) {
visited.insert(edge);
queue.push_back(SearchPath(edge, Some(path.clone())));
}
}
}
None
}
#[derive(Clone, PartialEq, Eq)]
pub struct SearchPath<N> (N, Option<Rc<SearchPath<N>>>);
impl<N: Debug> Debug for SearchPath<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match &self.1 {
Some(v) => write!(f, "{:?} -> {:?}", v, &self.0),
None => write!(f, "{:?}", &self.0)
}
}
}
Going further down the rabbit hole, we can do even more with this if we add some more type parameters. It may be a bit harder to read now, but what this lets us do is implement a bunch of different search approaches from graph theory using search as a base. Essentially, this new function search boils down the the core components of many search methods.
/// A general purpose graph search.
/// - start: Initial value to use in search queue
/// - expand: A function that takes a path, expands the edges of the top node,
/// then places the next elements in the queue according to the current search
/// approach. Additional ordering constraints may also be applied.
/// - next_node: A helper function which takes a path and evaluates if the search goal
/// has been reached. If the goal has been reached, None is returned and the current
/// path is returned. Otherwise, the top node in the given path is returned so
/// that it can be expanded.
fn search<N, P, F, R>(start: P, expand: F, next_node: R) -> Option<P>
where
N: Eq + Hash,
F: Fn(&P, &mut VecDeque<P>),
R: Fn(&P) -> Option<N>,
{
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(start);
while let Some(path) = queue.pop_front() {
let node = match next_node(&path) {
Some(v) => v,
None => return Some(path),
};
if visited.contains(&node) {
continue;
}
visited.insert(node);
expand(&path, &mut queue);
}
None
}
#[derive(Clone, PartialEq, Eq)]
pub struct WeightedSearchPath<N>(i32, N, Option<Rc<SearchPath<N>>>);
/// An example using search to find the most efficient path with weighted graph edges
fn weighted_search<N, F, R>(start: N, end: N, expand: F) -> Option<WeightedSearchPath<N>>
where
N: Copy + Eq + Hash,
F: Fn(N) -> R,
R: IntoIterator<Item=(i32, N)>,
{
search(
WeightedSearchPath(0, start, None),
|WeightedSearchPath(cost, node, path), queue| {
let path = Rc::new(SearchPath(*node, path.clone()));
for (weight, edge) in expand(*node) {
queue.push_back(WeightedSearchPath(cost + weight, edge, Some(path.clone())));
}
queue.make_contiguous().sort_by_key(|x| x.0);
},
|WeightedSearchPath(_, node, _)| {
if *node == end {
return None;
}
Some(*node)
},
)
}
I am trying to run this model but I keep getting this error. There is some mistake with regard to the shape of input data, I played around with it but I still get these errors.
Error:
ValueError: Input 0 of layer sequential is incompatible with the layer: expected axis -1 of input shape to have value 1 but received input with shape (None, 32, 32, 3)
# Image size
img_width = 32
img_height = 32
# Define X as feature variable and Y as name of the class(label)
X = []
Y = []
for features,label in data_set:
X.append(features)
Y.append(label)
X = np.array(X).reshape(-1,img_width,img_height,3)
Y = np.array(Y)
print(X.shape) # Output :(4943, 32, 32, 3)
print(Y.shape) # Output :(4943,)
# Normalize the pixels
X = X/255.0
# Build the model
cnn = Sequential()
cnn.add(keras.Input(shape = (32,32,1)))
cnn.add(Conv2D(32, (3, 3), activation = "relu", input_shape = X.shape[1:]))
cnn.add(MaxPooling2D(pool_size = (2, 2)))
cnn.add(Conv2D(32, (3, 3), activation = "relu",input_shape = X.shape[1:]))
cnn.add(MaxPooling2D(pool_size = (2, 2)))
cnn.add(Conv2D(64, (3,3), activation = "relu",input_shape = X.shape[1:]))
cnn.add(MaxPooling2D(pool_size = (2,2)))
cnn.add(Flatten())
cnn.add(Dense(activation = "relu", units = 150))
cnn.add(Dense(activation = "relu", units = 50))
cnn.add(Dense(activation = "relu", units = 10))
cnn.add(Dense(activation = 'softmax', units = 1))
cnn.summary()
cnn.compile(loss = 'categorical_crossentropy',optimizer = 'adam',metrics = ['accuracy'])
# Model fit
cnn.fit(X, Y, epochs = 15)e
I tried reading about this issue, but still didn't understand it very well.
your input shape should be (32,32,3). y is your label matrix. I assume it contains N unique integer values where N is the number of classes. If N=2 you can treat this as a binary classification problem. In that case your code for the top layer should be
cnn.add(Dense(1, activation = 'sigmoid'))
your code for compile should be
cnn.compile(loss = 'binary_crossentropy',optimizer = 'adam',metrics = ['accuracy'])
If you have more than 2 classes then your code should be
cnn.add(Dense(N, activation = 'softmax'))
cnn.compile(loss = 'sparse_categorical_crossentropy',optimizer = 'adam',metrics = ['accuracy'])
Where N is the number of classes,
Change this line (the last dimension):
cnn.add(keras.Input(shape = (32,32,3)))
I wish to detect an image from my webcam and write the result whether it is moving left or right to a file. To increase the frame rate(because this project involves a lot more processing and might be run on a raspberry pi) I decided to do the file writing part through multiprocessing (which I am new to):
code:
function for multi-processing
def send_cmd(cv):
# ####EXECUTE LOGIC AND CREATE COMMAND###
# writing to file
# just a sample command// change as will
dictionary = {'left': 0, 'right': 0, 'stop': 0}
if cv[0] == 'left':
dictionary['right'] = 1
else:
dictionary['left'] = 1
cmd = '{"left":' + str(dictionary['left']) + ',"right":' + str(dictionary['left'
]) + ',"stop":' + str(dictionary['left']) + '}'
print("command written: " + cmd)
f = open('command.txt', 'w')
f.write(cmd)
f.close()
Main code:
while True:
try:
frame = cv.VideoCapture(0)
frame = imutils.resize(frame, width=400)
if W is None or H is None:
(H, W) = frame.shape[:2]
blob = cv.dnn.blobFromImage(cv.resize(frame, (300, 300)),
0.007843, (300, 300), 127.5)
net.setInput(blob)
detections = net.forward()
rects = []
for i in range(0, detections.shape[2]):
if detections[0, 0, i, 2] > args['confidence']:
box = detections[0, 0, i, 3:7] * np.array([W, H, W, H])
rects.append(box.astype('int'))
(startX, startY, endX, endY) = box.astype('int')
cv.rectangle(frame, (startX, startY), (endX, endY), (0,
0xFF, 0), 2)
objects = ct.update(rects)
for (objectID, centroid) in objects.items():
text = 'ID {}'.format(objectID)
cv.putText(
frame,
text,
(centroid[0] - 10, centroid[1] - 10),
cv.FONT_HERSHEY_SIMPLEX,
0.5,
(0, 0xFF, 0),
2,
)
cv.circle(frame, (centroid[0], centroid[1]), 4, (0, 0xFF,
0), -1)
center = (centroid[0], centroid[1])
pts.appendleft(center)
for i in np.arange(1, len(pts)):
if pts[i - 1] is None or pts[i] is None:
continue
if counter >= 10 and i == 1 and pts[-1] is not None:
dX = pts[-1][0] - pts[i][0]
dY = pts[-1][1] - pts[i][1]
global dirX
global dirY
(dirX, dirY) = ('', '')
if np.abs(dX) > 20:
dirX = ('left' if np.sign(dX) == 1 else 'right')
if np.abs(dY) > 20:
dirY = ('up' if np.sign(dY) == 1 else 'down')
#tried multiprocessing with process method but to many process calls at the same time
order = multiprocessing.Process(target = send_cmd,args = ([dirX, dirY]))
order.start()
order.join()
# send_cmd(cv=[dirX, dirY], us=ultra_sonic)
if dirX != '' and dirY != '':
direction = '{}-{}'.format(dirY, dirX)
else:
direction = (dirX if dirX != '' else dirY)
thickness = int(np.sqrt(args['buffer'] / float(i + 1))
* 2.5)
cv.putText(
frame,
direction,
(10, 30),
cv.FONT_HERSHEY_SIMPLEX,
0.65,
(0, 0, 0xFF),
3,
)
cv.putText(
frame,
'dx: {}, dy: {}'.format(dX, dY),
(10, frame.shape[0] - 10),
cv.FONT_HERSHEY_SIMPLEX,
0.35,
(0, 0, 0xFF),
1,
)
cv.imshow('Frame', frame)
key = cv.waitKey(1) & 0xFF
counter += 1
Error:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
Can someone guide me?
I have a MLP coded using Tensorflow Python API. The following is the code snippet:
# tf Graph input
x = tf.placeholder("float", [None, 11],name="x")
y = tf.placeholder("float", [None])
# Store layers weight & bias
weights = {
'h1': tf.Variable(tf.random_normal([11, 32], 0, 0.1)),
'h2': tf.Variable(tf.random_normal([32, 200], 0, 0.1)),
'out': tf.Variable(tf.random_normal([200, 1], 0, 0.1))
}
biases = {
'b1': tf.Variable(tf.random_normal([32], 0, 0.1)),
'b2': tf.Variable(tf.random_normal([200], 0, 0.1)),
'out': tf.Variable(tf.random_normal([1], 0, 0.1))
}
# Create model
def multilayer_perceptron(x, weights, biases):
# Hidden layer with RELU activation
layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1'])
layer_1 = tf.nn.relu(layer_1)
# Hidden layer with RELU activation
layer_2 = tf.add(tf.matmul(layer_1, weights['h2']), biases['b2'])
layer_2 = tf.nn.relu(layer_2)
# Output layer with linear activation
out_layer = tf.matmul(layer_2, weights['out']) + biases['out']
return out_layer
# Construct model
pred = multilayer_perceptron(x, weights, biases)
pred = tf.identity(pred, name="pred")
The model has been trained and saved using the saved_model_builder.SavedModelBuilder method. The predictions using the Python API can be done using the following code:
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
tf.saved_model.loader.load(sess, ["tag"], "/tmp/saved_models")
my_pred= sess.graph.get_tensor_by_name('pred:0')
predictions = sess.run(my_pred, feed_dict={x: pred_data})
print("The prediction is", predictions)
I am trying to make the same predictions using the Go API using the following code snippet:
df := []float32{9.5,0.0,7.5,0.0,0.0,2.0,0.0,0.0,0.0,0.0,1505292248.0}
tensor, terr := tf.NewTensor(df)
result, runErr := model.Session.Run(
map[tf.Output]*tf.Tensor{
model.Graph.Operation("x").Output(0): tensor,
},
[]tf.Output{
model.Graph.Operation("pred").Output(0),
},
nil,
)
However, I encounter with the following error:
Error running the session with input, err: In[0] is not a matrix
[[Node: MatMul = MatMul[T=DT_FLOAT, _output_shapes=[[?,32]], transpose_a=false, transpose_b=false, _device="/job:localhost/replica:0/task:0/cpu:0"](_arg_x_0_0, Variable/read)]]
Can someone point out the reason for this error?
The error is clear: In[0] is not a matrix.
Your In[0] is: df := []float32{9.5,0.0,7.5,0.0,0.0,2.0,0.0,0.0,0.0,0.0,1505292248.0}
This is a 1-dimensional tensor, not a matrix.
The matmul node requires both its arguments to be matrices, thus 2-dimensional tensors.
Therefore, you have to change your df definition in order to define a 2-D tensor, like that:
df := [][]float32{{9.5},{0.0},{7.5},{0.0},{0.0},{2.0},{0.0},{0.0},{0.0},{0.0},{1505292248.0}}
A good reference on how to think/debug tensorflow + go code is: https://pgaleone.eu/tensorflow/go/2017/05/29/understanding-tensorflow-using-go/
I am playing with the new stuff of C#3.0 and I have this code (mostly taken from MSDN) but I can only get true,false,true... and not the real value :
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var oddNumbers = numbers.Select(n => n % 2 == 1);
Console.WriteLine("Numbers < 5:");
foreach (var x in oddNumbers)
{
Console.WriteLine(x);
}
How can I fix that to show the list of integer?
Change your "Select" to a "Where"
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var oddNumbers = numbers.Where(n => n % 2 == 1);
Console.WriteLine("Odd Number:");
foreach (var x in oddNumbers)
{
Console.WriteLine(x);
}
The "Select" method is creating a new list of the lambda result for each element (true/false). The "Where" method is filtering based on the lambda.
In C#, you could also use this syntax, which you may find clearer:
var oddNumbers = from n in numbers
where n % 2 == 1
select n;
which the compiler translates to:
var oddNumbers = numbers.Where(n => n % 2 == 1).Select(n => n);
numbers.Select(n => n % 2 == 1);
Change this to
numbers.Where(n => n % 2 == 1);
What select does is "convert" one thing to another. So in this case, it's "Converting" n to "n % 2 == 1" (which is a boolean) - hence you get all the true and falses.
It's usually used for getting properties on things. For example if you had a list of Person objects, and you wanted to get their names, you'd do
var listOfNames = listOfPeople.Select( p => p.Name );
You can think of this like so:
Convert the list of people into a list of strings, using the following method: ( p => p.Name)
To "select" (in the "filtering" sense of the word) a subset of a collection, you need to use Where.
Thanks Microsoft for the terrible naming