SlideShare a Scribd company logo
Openvx Programming Guide 1st Edition Frank Brill
download
https://ebookbell.com/product/openvx-programming-guide-1st-
edition-frank-brill-11109984
Explore and download more ebooks at ebookbell.com
Here are some recommended products that we believe you will be
interested in. You can click the link to download.
Openx Ad Server Beginners Guide Murat Yilmaz
https://ebookbell.com/product/openx-ad-server-beginners-guide-murat-
yilmaz-2310212
Openvz Essentials Create And Administer Virtualized Containers On Your
Server Using The Robust Openvz Mark Furman
https://ebookbell.com/product/openvz-essentials-create-and-administer-
virtualized-containers-on-your-server-using-the-robust-openvz-mark-
furman-5472116
Openvx Programming Guide 1st Edition Frank Brill
OPENVX
PROGRAMMING
GUIDE
Openvx Programming Guide 1st Edition Frank Brill
OPENVX
PROGRAMMING
GUIDE
FRANK BRILL
VICTOR ERUKHIMOV
RADHAKRISHNA GIDUTHURI
STEPHEN RAMM
Academic Press is an imprint of Elsevier
125 London Wall, London EC2Y 5AS, United Kingdom
525 B Street, Suite 1650, San Diego, CA 92101, United States
50 Hampshire Street, 5th Floor, Cambridge, MA 02139, United States
The Boulevard, Langford Lane, Kidlington, Oxford OX5 1GB, United Kingdom
Copyright © 2020 Elsevier Inc. All rights reserved.
No part of this publication may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording, or any information storage and
retrieval system, without permission in writing from the publisher. Details on how to seek
permission, further information about the Publisher’s permissions policies and our arrangements
with organizations such as the Copyright Clearance Center and the Copyright Licensing Agency,
can be found at our website: www.elsevier.com/permissions.
This book and the individual contributions contained in it are protected under copyright by the
Publisher (other than as may be noted herein).
Notices
Knowledge and best practice in this field are constantly changing. As new research and experience
broaden our understanding, changes in research methods, professional practices, or medical
treatment may become necessary.
Practitioners and researchers must always rely on their own experience and knowledge in
evaluating and using any information, methods, compounds, or experiments described herein. In
using such information or methods they should be mindful of their own safety and the safety of
others, including parties for whom they have a professional responsibility.
To the fullest extent of the law, neither the Publisher nor the authors, contributors, or editors,
assume any liability for any injury and/or damage to persons or property as a matter of products
liability, negligence or otherwise, or from any use or operation of any methods, products,
instructions, or ideas contained in the material herein.
Library of Congress Cataloging-in-Publication Data
A catalog record for this book is available from the Library of Congress
British Library Cataloguing-in-Publication Data
A catalogue record for this book is available from the British Library
ISBN: 978-0-12-816425-9
For information on all Academic Press publications
visit our website at https://www.elsevier.com/books-and-journals
Publisher: Mara Conner
Acquisitions Editor: Tim Pitts
Editorial Project Manager: Joshua Mearns
Production Project Manager: Kamesh Ramajogi
Designer: Miles Hitchen
Typeset by VTeX
Contents
About the authors xi
Foreword xiii
Acknowledgments xv
1. Introduction 1
1.1. What is OpenVX and why do we need it? 2
1.2. Portability 4
1.3. OpenVX data objects 5
1.3.1. Opaque memory model 5
1.3.2. Object attributes 6
1.4. Graph API 6
1.5. Virtual objects 8
1.6. Deep neural networks 9
1.7. Immediate mode API 9
1.8. OpenVX vs. OpenCV and OpenCL 10
1.9. OpenVX versions 12
1.10. Prerequisites 12
1.11. Code samples 12
2. Build your first OpenVX program 15
2.1. First things first 15
2.1.1. How to get the OpenVX sample implementation 16
2.1.2. Building the examples 16
2.2. Immediate mode 17
2.2.1. Our first very simple example in immediate mode 17
2.3. Graph mode 22
2.3.1. Creating the graph 22
2.3.2. Connecting to parameters and running the graph 24
2.3.3. Running example 4 25
2.3.4. Code for example 4 25
2.4. Image input and output 30
2.4.1. Converting to and from portable pixmap format 30
2.4.2. Other libraries for input and output of images 31
2.4.3. Image processing exercises 31
2.4.4. Answers, or at least hints, for the exercises 32
3. Building an OpenVX graph 35
3.1. Linking nodes 36
3.2. Virtual images 37
v
vi Contents
3.3. Graph verification and execution 39
3.4. Parameter validation 43
3.5. What can be changed at runtime? 44
3.6. Example 46
4. Using the graph API to write efficient portable code 47
4.1. OpenVX graph 48
4.1.1. OpenVX graph is a bipartite DAG 48
4.1.2. Intermediate results as virtual data objects 49
4.1.3. A graph example 49
4.2. Node parameters 52
4.3. Graph parameters 53
4.4. Execution model 55
4.4.1. Synchronous execution 55
4.4.2. Asynchronous execution 57
4.5. Control flow 58
4.6. User kernels and nodes 58
4.7. The OpenVX pipelining extension 60
5. Deploying an OpenVX graph to a target platform 65
5.1. Graph factories 66
5.1.1. An example of a graph factory 66
5.1.2. Using a graph factory 69
5.1.3. When to use a graph factory for deployment 71
5.1.4. Graph factory pros and cons 71
5.2. The XML extension 72
5.2.1. Pros and cons of the XML extension 74
5.2.2. When to use the XML extension for deployment 75
5.3. The export and import extension 75
5.3.1. Exporting a graph with the export and import extension 75
5.3.2. Importing a graph with export and import extension 78
5.3.3. Pros and cons of the export and import extension 81
5.3.4. When to use the export and import extension 82
6. Basic image transformations 85
6.1. OpenVX image object 85
6.2. Image filtering 87
6.2.1. Simple image filtering example 87
6.2.2. Custom convolution 89
6.3. Regions of interest 91
6.3.1. Reading from regions of interest 91
6.3.2. Writing to regions of interest 92
6.4. Feature extraction 95
Contents vii
6.4.1. Hough transform 95
6.4.2. Postprocessing hough transform results 100
6.5. Geometric image transformations 108
6.5.1. Undistortion implemented with remap 108
6.5.2. Perspective transformations 114
7. Background subtraction and object detection 125
7.1. A threshold-based object detector 125
7.2. Adding alpha blending to the background model 129
7.3. Applying morphology operators to the foreground 133
8. Computational photography 135
8.1. Naïve image blending 135
8.1.1. The algorithm 136
8.1.2. Preparing stitching data 137
8.1.3. The stitching algorithm implementation 139
8.2. Obtaining intermediate results 143
8.3. Multiband image blending 145
8.3.1. The main graph 148
8.4. Building a Gaussian pyramid for the blending weights 153
8.5. Implementing Laplacian pyramid functions with OpenVX graph 157
8.5.1. Implementing a Laplacian pyramid 157
8.5.2. Implementing Laplacian pyramid reconstruction 160
9. Tracking 163
9.1. Introduction 163
9.2. Tracking methods 164
9.3. Optical flow 165
9.4. The aperture problem 166
9.5. The Lucas–Kanade algorithm for sparse feature sets 166
9.6. Bouguet’s implementation using image pyramids 168
9.6.1. The OpenVX API for optical flow 169
9.7. Selection of features to track 171
9.7.1. The OpenVX API for FAST corners 172
9.8. Centroid tracking 172
9.8.1. Difficulties 174
9.8.2. User nodes to handle centroid tracking 175
9.9. Example of tracking using OpenVX 177
9.9.1. Calculating the initial tracking data 180
9.9.2. Calculating the bounding box position in the next frame 185
9.9.3. Running the tracking example application 194
9.10. Multiple objects 195
9.11. Occlusion and trajectories 195
viii Contents
9.12. The mirror ball and other failings of our algorithm 197
9.13. Image stabilization, panning, and zooming 198
10. Neural networks 205
10.1. OpenVX neural network extension 206
10.2. Inserting an entire neural network into an OpenVX graph as a single
node 207
10.3. NNEF: the Khronos neural network exchange format 208
10.4. Import NNEF models into OpenVX 209
10.4.1. Inserting a neural network into an OpenVX graph 210
10.4.2. Summary 214
11. OpenVX safety-critical applications 215
11.1. Safety-critical applications and standards 216
11.1.1. What sort of applications have safety implications? 216
11.1.2. Applicable standards 218
11.2. What is in these standards? 222
11.3. What could possibly go wrong? 223
11.3.1. A spelling mistake with potentially disastrous consequences 223
11.3.2. Misuse of enumerated types 223
11.4. Errors and avoiding them 223
11.4.1. Requirement, design, programming and transient errors 223
11.4.2. Defensive programming 225
11.4.3. Feature subsets 226
11.5. How to do agile development and still meet the ISO26262 requirements 226
11.6. Determinism 227
11.7. OpenVX-SC 228
11.7.1. Differences between OpenVX and OpenVX-SC 229
11.7.2. Drawbacks of OpenVX-SC 230
11.7.3. A targeted shim layer 233
11.8. Programming with C++ 233
11.9. Data objects 234
11.9.1. Constructors and destructors 234
11.9.2. Query attribute and SetAttribute 234
11.9.3. Arrays of references 235
11.10. A practical example 235
11.10.1. vx_reference 236
11.10.2. VxReference with paranoia 240
11.10.3. queryReference and the tagmap template 240
11.10.4. Arrays of references 242
11.10.5. vx_context – the VxContext object 244
11.10.6. vx_import 249
11.10.7. vx_graph 249
Contents ix
11.10.8. Mapping other objects 256
11.10.9. The delay and object array classes 256
11.11. Learning and modification of software in safety-critical systems 258
12. Efficient data input/output 265
12.1. Introduction to efficient data I/O in OpenVX 265
12.2. Efficient data I/O from and to external sources 266
12.3. Gaining access to data objects via map and unmap 273
12.3.1. Mapping image patches 274
12.3.2. More on pixel addressing 277
12.3.3. Mapping arrays 280
12.3.4. Mapping distributions 281
12.3.5. Mapping LUTs 282
12.3.6. Mapping remap objects 282
12.3.7. Mapping tensors 284
12.4. Conclusion 285
13. Interoperability between OpenVX and other frameworks 287
13.1. OpenCV 288
13.1.1. Convert an OpenCV image to OpenVX 289
13.1.2. Convert an OpenVX image to OpenCV 292
13.1.3. Converting other data types 294
13.2. OpenCL 296
13.2.1. OpenCL in a nutshell 296
13.2.2. Interoperability with OpenCL 302
13.2.3. Summary 308
14. Existing implementations and platforms for OpenVX 311
14.1. CPU implementations 312
14.2. DSP implementations 314
14.3. GPU implementations 316
14.4. Special-purpose hardware implementations 318
14.5. FPGA implementations 318
14.6. Hybrid System-on-Chips (SoCs) 319
15. Changes in OpenVX 1.3 321
15.1. Feature sets 321
15.1.1. Binary images 323
15.2. Safety-critical application support 325
15.3. Removal of bidirectional parameters 327
15.4. User structures 328
15.5. Miscellaneous changes 328
x Contents
A. The list of OpenVX data objects and their attributes 331
B. The list of computer vision graph API nodes 337
B.1. Image arithmetics and control flow 337
B.2. Image format conversions 338
B.3. Image filtering and statistics 339
B.4. Geometric transformations 339
B.5. Feature extraction 340
B.6. Tensor functions 341
References 343
Index 347
About the authors
Frank Brill manages Vision and AI software development for Cadence’s
Tensilica Imaging and Vision DSP organization. He began his professional
career developing network management software at Bell Communications
Research. He then returned to school to earn a Ph.D., which he took
to Texas Instruments to do computer vision research and development for
video surveillance applications. He then moved into silicon device pro-
gram management, where he was responsible for several systems-on-chip
for digital still cameras and other multimedia devices. Since then, Frank
has managed computer vision R&D groups at TI, NVIDIA, Samsung, and
now at Cadence, and has represented all four companies in the Khronos
OpenVX working group. He joined Cadence in 2016 and served as the
chairperson of the OpenVX working group from 2016 to 2019. Frank
holds Bachelor’s degrees in Mathematics and Computer Science from the
University of Alaska, Fairbanks, and Master’s and Ph.D degrees in Com-
puter Science from the University of Virginia.
Victor Erukhimov is currently the CEO of Itseez3D, the company that
democratizes 3D model creation. He also cofounded Itseez, Inc., which
focused on developing computer vision solutions running on embedded
platforms, specifically automotive safety systems. He successively held the
positions of CTO, CEO, and President at Itseez when the company was ac-
quired by Intel Corporation in 2016. Victor was the chair of the OpenVX
working group in 2012–2016, helping to create the standard for cross-
platform computer vision API. Before moving to the field of computer
vision, he studied plasma physics at the Institute of Applied Physics, Russian
Academy of Sciences, and tried to solve (without much success) the prob-
lem of electron cyclotron heating. Victor was a member of the OpenCV
development team. He participates in the development and maintenance of
the OpenCV library and serves as the director of the OpenCV Foundation
board. He is the author of about 30 papers and several US and international
patents.
Radhakrishna Giduthuri is currently a Principal Engineer at Intel, fo-
cusing on software architecture for Intel AI Accelerators. Prior to working
at Intel, he built computer vision, deep learning, and video compres-
sion software acceleration libraries for AMD GPUs & CPUs. He has an
xi
xii About the authors
extensive background with software architecture, development, and per-
formance tuning for various computer architectures ranging from general
purpose DSPs, customizable DSPs, media processors, heterogeneous pro-
cessors, GPUs, and several CPUs. He is the current editor of the Khronos
OpenVX working group and a member of Khronos NNEF (Neural Net-
work Exchange Format) and OpenCL Safety-Critical working groups. For
several years, he was a member of the SMPTE Video Compression Stan-
dardizing Committee. He was the Outstanding Leadership and Professional
Services Award Recipient for the IEEE Central Area in 2016. Radhakr-
ishna earned a Master’s degree from IIT Kharagpur, India.
Stephen Ramm is currently a principal software engineer with ETAS,
a subsidiary of Bosch, where he is working on reliable frameworks and
development environments for advanced functionality in the automotive,
rail and other safety-critical industries. Until late 2017, he was Director
of AI and Vision software at Imagination Technologies, where one of his
responsibilities was the team producing an implementation of OpenVX ac-
celerated by proprietary GPU architecture. Prior to that, Stephen has held
various positions in technical management, research, design, and applied
mathematics with experience in fields as varied as optical design, nutri-
tional analysis, advanced statistics and calibration techniques, RF electron-
ics, NMR, seismic analysis and detonators, electronics at board and silicon
level, and development tools for the games industry. Stephen was formerly
an editor of the OpenVX specification, the former chair of the OpenVX
Safety-critical subcommittee, and a former member of the OpenGL Safety
Critical group and Safety Critical Advisory Panel within Khronos. He stud-
ied Physics and Computer Science at York University in the UK, and is a
member of the Institute of Physics and a Chartered Physicist.
Foreword
While preparing to write this preface I was reminded that I have been
involved in creating standards for image and vision processing for thirty
years, and the work is not yet done. But now, finally, the Khronos Group’s
OpenVX API for vision and inferencing is established as the industry’s lead-
ing open standard for effectively connecting vision developers to the power
of acceleration silicon, enabling a new generation of applications that are
visually aware.
Why did it take so long to architect an effective vision acceleration API?
The earliest efforts took the most obvious approach, simply listing all the
potentially useful image and vision operators and creating an API function
call for each—done! But not so fast! In hindsight, that approach was too
simplistic and suffered from two fatal issues. Firstly, the list of potential func-
tions grew unsustainably large, exacerbated by the combinatorial explosion
of different input and output parameter types. Those early specifications be-
came lists of hundreds (and hundreds) of functions, an impossibly large and
diffuse API surface area for silicon vendors to implement effectively. Sec-
ondly, sending each function to the hardware in isolation did not provide
the silicon implementors enough context to undertake holistic optimiza-
tions. Each function call would have to read inputs and write results to
default memory positions, with no way to know what operations had gone
before and what was about to follow, leaving huge amounts of potential
optimizations on the table.
OpenVX’s key innovation is enabling application developers to holis-
tically describe the complete flow of vision operators in their application
as a graph connecting a network of nodes, each node defining a vision
function from a carefully curated menu. Handing the complete graph to
the silicon enables a groundbreaking level of optimization, for example,
avoiding memory round trips or slicing and dicing images to fit in cache
memory. Best of all, the graph description is high-level enough to be hard-
ware agnostic, so each processor vendor can optimize the graph; however,
they wish to execute optimally on their accelerator architecture, and they
have all the information they need to do so.
But that is not all. An additional key ingredient in OpenVX’s success
derives from Khronos’ long expertise in creating specifications that are
carefully crafted, and conformance tested, to enable a vibrant ecosystem
xiii
xiv Foreword
containing multiple OpenVX implementations, each optimized for a par-
ticular silicon processor while still providing application portability. This is
directly analogous to GPU vendors shipping highly optimized 3D drivers
for their silicon. It is in contrast to opensource projects where a single li-
brary implementation is ported across many platforms—and so may be less
optimized than dedicated out-of-the-box vendor drivers.
It is a testament to the strength of OpenVX’s underlying design that
while early OpenVX implementations were gaining their first users, deep
learning burst onto the scene, providing a new and powerful technique
for solving complex pattern-matching and image recognition problems.
OpenVX was able to leverage its extensible architecture to rapidly in-
corporate neural network inferencing into its graph description through
a new tensor data type and associated operations. Today, OpenVX provides
a uniquely powerful framework that can mix and combine both traditional
image operators and hardware accelerated inferencing.
Now here we are in 2020, and we have the recently released OpenVX
1.3, which both consolidates proven extensions into the core specifica-
tion and provides significant vertical market-targeted deployment flexibility
through feature sets. OpenVX now even enables accelerated extensions to
be integrated into an application’s graph—an elegant solution to the “100s
of functions” problem that plagued the early attempts at vision library stan-
dardization.
Optimized OpenVX implementations are shipping from multiple hard-
ware vendors and routinely delivering superior performance to hand-
crafted vision processing code with far great portability and significantly
reduced development costs.
Strong open standards such as OpenVX result from a wide need for a
proven technology in the industry, and in the long view, an open standard
that is not controlled by, or dependent on, any single company can often
be the thread of continuity for industry forward progress as technologies,
platforms, and market positions swirl and evolve. Open standards need the
passion, patience, and participation of those that understand their value.
OpenVX has been fortunate indeed in benefitting from the passion and
patience of the authors of this book, that it has been my pleasure and honor
to have worked with, as they have played a central role in creating the
OpenVX ecosystem and continue to nurture and evolve this key vision
acceleration standard into the future. The thirty-year journey continues.
Neil Trevett, President of the Khronos Group
Acknowledgments
We thank all of our current and former colleagues at the OpenVX com-
mittee for their hard and productive work on the standard. This standard
would not be possible without Khronos President Neil Trevett, who guided
and inspired us, and whose wonderful sense of humor saved us many times
when the group was stuck trying to reach a consensus. The anonymous
book reviewers provided useful comments on the book structure, and we
want to thank Dmitry Matveev and Jesse Villarreal for the feedback on the
contents. Our special thanks go to Susheel Gautam, who was the editor of
the OpenVX 1.0 and 1.1 specifications, Erik Rainey, who was the editor
of OpenVX 1.0 specification and the developer of the first version of the
OpenVX sample implementation, and Kari Pulli, who helped to create the
standard and was instrumental in promoting it. We are also very grateful to
our families, who tolerated us spending long hours on weekends writing
this book.
xv
CHAPTER 1
Introduction
Contents
1.1. What is OpenVX and why do we need it? 2
1.2. Portability 4
1.3. OpenVX data objects 5
1.3.1 Opaque memory model 5
1.3.2 Object attributes 6
1.4. Graph API 6
1.5. Virtual objects 8
1.6. Deep neural networks 9
1.7. Immediate mode API 9
1.8. OpenVX vs. OpenCV and OpenCL 10
1.9. OpenVX versions 12
1.10. Prerequisites 12
1.11. Code samples 12
OpenVX1 [1] is an Application Programming Interface (API) that was cre-
ated to make computer vision programs run faster on mobile and embedded
devices. It is different from other computer vision libraries, because from
the very beginning it was designed as a Hardware Abstraction Layer (HAL)
that helps software run efficiently on a wide range of hardware platforms.
OpenVX is developed by the OpenVX committee, which is a part of the
Khronos Group [2]. Khronos is an open and nonprofit consortium; any
company or individual can become its member and participate in the de-
velopment of OpenVX and other standards, including OpenGL, Vulkan,
and WebGL. OpenVX is royalty free, but at the same time, it is developed
by the industry: some of the world largest silicon vendors are members of
the OpenVX committee.
If you are impatient to get started with OpenVX, you might want to
skip to the next chapter. The introduction discusses the challenges of run-
ning computer vision algorithms in real-time that OpenVX attempts to
solve. It will help you get familiar with the OpenVX high-level concepts,
such as objects with opaque memory model and the OpenVX Graph.
1 OpenVX is a Khronos Group trademark.
OpenVX Programming Guide
https://doi.org/10.1016/B978-0-12-816425-9.00007-3
Copyright © 2020 Elsevier Inc.
All rights reserved. 1
2 OpenVX Programming Guide
1.1 What is OpenVX and why do we need it?
Computer vision can be defined as extracting high-level information from
images and video. Nowadays it is in the process of changing many in-
dustries, including automotive (Advanced Driver Assistance Systems, self-
driving cars), agriculture and logistics (robotics), surveillance and banking
(face recognition), and many more. A significant part of these scenarios
require a computer vision algorithm to run in real time on a low-power
embedded hardware. A pedestrian detection algorithm running in a car has
to process each input frame; a delay makes a car less safe. For example, in
a car moving at 65 miles per hour, each skipped frame adds about 3 feet
to the braking distance. A robot that makes decisions slowly can become
a bottleneck in the manufacturing process. AR and VR glasses that track
their position slower than 30 frames per second cause motion sickness for
many users.
Solving a practical computer vision problem in real time is usually a
challenging task. The algorithms are much more complicated than running
a single linear filter over an image, and image resolution is high enough to
cause a bottleneck in a memory bus. In many cases, significant speedups can
be reached by choosing the right algorithm. For example, the Viola–Jones
face detector algorithm [3] enables detection of human faces in real time on
relatively low-power hardware. The FAST [4] feature detector and BRIEF
[5] and ORB [6] descriptors allow quick generation of features for tracking.
However, in the most of cases, it is not possible to achieve the required
performance by just algorithmic optimizations.
The concept of optimizing a computer vision pipeline for specific hard-
ware is not new to the community, to say the least. One of the first major
efforts in this direction was done by Intel, which released the first version of
OpenCV library [7] in 2000 along with the optimized layer for Intel CPUs.
Other hardware vendors provided their libraries too (see, e.g., [8–10]), and
others followed with developing dedicated hardware for computer vision
processing, such as the Mobileye solution for automotive [11]. NVIDIA
GPUs with their high level of parallelism, and a wide bandwidth memory
bus enabled processing of deep learning algorithms that, at the time of writ-
ing this book, are the best-known methods of solving a range of computer
vision tasks, from segmentation to object recognition.
The computer vision applications on mobile/embedded platforms dif-
fer from server-based applications: real-time operation is critical. If a web
image search algorithm keeps a user waiting, then it is still useful, and
we can throw in more servers to make it faster. This is not possible for
Introduction 3
a car, where many computer vision tasks (pedestrian detection, forward car
collision warning, lane departure warning, traffic sign recognition, driver
monitoring) have to work in real time on a relatively low-power hardware.
In this context, computer vision optimization for mobile/embedded appli-
cations is much more important than for server applications. This is why
OpenVX is focused on mobile and embedded platforms, although it can
be efficiently implemented for the cloud too.
Low-power mobile and embedded architectures typically are heteroge-
neous, consisting of a CPU and a set of accelerators dedicated to solving a
specific compute intense problem, such as 3D graphics rendering, video
coding, digital signal processing, and so on. There are multiple challenges
for computer vision developers who want to run their algorithms on such
a system. A CPU is too slow to run compute- and data-intense algorithms
such as pedestrian detection in real time. One of the reasons is that a mem-
ory bus has a relatively low throughput, which limits multicore processing.
Executing code on GPU and DSP can help, but writing code that will
employ such processing units efficiently across multiple vendors is next to
impossible. GPUs and DSPs have no standard memory model. Some of
them share RAM with CPU, and some have their own memory with a sub-
stantial latency for data input/output. There are ways to write code that will
execute on both CPU and GPU, such as [12]. However, developers want
higher-level abstraction, which may be implemented over low-level API,
such as OpenCL, or coded directly in the hardware. Chapter 13 discusses
how OpenVX and OpenCL code can coexist and benefit from each other.
Also, there are architectures that do not have full IEEE 754 floating point
calculations support (required in the current version of OpenCL); they im-
plement fixed-point arithmetic instead (where a real number is represented
with a fixed amount of digits after the radix point). Finding a balance be-
tween precision and speed on such architectures is a serious challenge, and
we have to tune an algorithm for such platforms. Computer vision is too
important to allow such an overhead, and there has to be a way to write an
algorithm that will run efficiently on a wide variety of accelerators, imple-
mented and tuned for a specific mobile or embedded platform. This is the
problem solved by OpenVX. The OpenVX library is usually developed,
optimized, and shipped by silicon vendors, just like a 3D driver for a GPU.
It has a graph API, which is convenient to use and efficient for executing
on heterogeneous platforms. One of the most important requirements for
this API is its portability across platforms.
4 OpenVX Programming Guide
1.2 Portability
OpenVX is the API that allows a computer vision developer to write a
program once and have it efficiently executed on hardware, assuming that
an efficient OpenVX implementation exists for that hardware. A reasonable
expectation for this API is that it produces the same results across different
platforms. But what does “same results” actually mean? How do we test if
two implementations of the same function return the same results? There
are several ways to answer this question:
• Bit-exact tests: the output of an implementation should not differ from
the ground truth by a single bit. Such a test is useful to ensure that
the results are going to be the same. However, there are many cases
where bit-exact tests make no sense. For example, a simple function
for correcting camera lens distortion [13] uses floating point calcula-
tions, and even if a method to compute an image transformation is
well specified, a bit exact requirement may be too strong. For instance,
a conformance to IEEE 754 floating point calculation standard may re-
sult in a very inefficient implementation on a fixed-point architecture.
So, in many cases, it is beneficial to allow for a limited variety in the
results.
• Tolerance-based comparison: for example, we can require that the output
image is not different from the reference implementation output by
more than  in each pixel and each channel. There is no exact science
in choosing the value of the threshold . The higher the threshold, the
more space there is for optimizing functions for a specific platform, and
the higher the variability of the results across different platforms. Com-
puter vision means extracting high-level information from images, and
if greater variability in this high-level information prevents solving the
problem, then the thresholds are too high. For instance, an image arith-
metic operation involved in an image stitching algorithm that results in
a high-quality image on one platform and produces a visible stitch line
on another should not pass a tolerance-based test.
• Algorithmic tests: a useful test to ensure a function returns correct
results is to test some properties of this result. For instance, a face de-
tector should return an image rectangle that contains a face. A text
segmentation algorithm should return areas in an image that contain
text. Although algorithmic tests are useful for any computer vision li-
brary, they usually are a poor choice for ensuring portability. Whereas
a face detector returns a rectangle with a face, the algorithm processing
this rectangle can work differently depending on the size and position
Introduction 5
of the rectangle. If the rectangle parameters vary across platforms, then
the overall results may also depend on the platform.
As we can see, portability is a fine balance between precision and op-
timization. A set of tests that check for portability called conformance tests is
an essential part of any cross-platform standard. Conformance tests are dif-
ferent from algorithmic tests: their objective is not only to check whether a
function returns results according to the specification, but also to ensure the
results to be consistent across different platforms. This is why conformance
tests usually consist of bit-exact and tolerance-based tests, rarely using algo-
rithmic tests.
OpenVX has its own set of conformance tests [14,15], designed by
the OpenVX committee and available to all implementers of the standard.
Passing conformance tests is an essential requirement for an implementation
to be called “OpenVX.”
1.3 OpenVX data objects
OpenVX defines several objects that are needed for common computer
vision algorithms. This list includes images, pyramids, matrices, tensors, and
several other constructs. All the data objects share some high-level design
properties.
1.3.1 Opaque memory model
An efficient cross-platform computer vision API should be able to exe-
cute the same function on different accelerators. A function implementing
a stereo matching algorithm should be computed on a GPU, provided that
a GPU is present on the platform and is powerful enough to execute this
function faster than a CPU. However, a GPU has its own memory, and
executing stereo matching on a GPU will require moving image data to
the GPU memory. Giving a user control over memory that stores image
data requires exposing a memory model for a wide range of embedded ac-
celerator memory models that OpenVX is targeting. This is why the image
object in OpenVX is opaque, meaning that a user has no direct access to
pixels other than through dedicated OpenVX functions. The same design
principle is applied to all OpenVX data objects. This allows an implemen-
tation of OpenVX to change data layout and to move data between the
CPU memory, called host memory throughout the OpenVX spec, and the
accelerator memory. The access to data is provided by a pair of Map/Un-
map functions defined for each object, which create a mapping between
6 OpenVX Programming Guide
object data and a region in host memory. For example, the functions for
accessing image pixels are called vxMapImagePatch and vxUnmapImagePatch.
1.3.2 Object attributes
Each object, in addition to the data it holds, has parameters that define
its contents. For example, an image is characterized by width and height,
as well as its format (the number of color planes, color format, etc.).
These parameters are called attributes throughout the spec. Some of the
attributes have to be specified when an object is being created and can-
not be changed by a user during the object lifetime. Such attributes are
referred to as “Read-only” in the spec. Attributes that can be changed
are marked as “Read-write.” Values of object attributes can be retrieved
with a “query” function, usually named vxQuery[Object], for example,
vxQueryImage for the image object. The read-write attributes can be changed
with a “set” function, usually named vxSet[Object]Attribute, for example,
vxSetImageAttribute for the image object. Appendix A contains the list of
all OpenVX data objects along with their attributes.
1.4 Graph API
As we discussed before, data objects in OpenVX can be stored in the host
memory and accelerator memory. However, moving image data between
devices can be time consuming. If we are to run a single image filtering
operation, then a GPU may be faster to do the job than a CPU, but the
overhead of moving image data between the host and GPU memory may
negate all the speedup. Making such decisions requires knowledge about
the platform. If we intend to execute an OpenVX-based algorithm on a
wide variety of platforms (such as Android phones), all kinds of accelerators
available on these platforms, as well as memory and data transfer between
accelerators, need to be taken into account. It is practically impossible to
build this level of decision making into the code. It is more realistic to
expect a manufacturer of a specific hardware compute platform to take into
account all this special knowledge in an efficient OpenVX implementation.
This is why an OpenVX implementation manages where and when specific
functions are going to be executed, and a user has practically no control over
it. As a result, OpenVX provides an abstraction level that allows us to write
code once and execute it efficiently on many different embedded platforms
where the API is implemented.
Introduction 7
Figure 1.1 An example of an OpenVX graph. Graph nodes represent functions, and the
directed links define input and output data.
Since an OpenVX implementation has to make decisions on how to
execute each function, we need to provide it with some context in advance,
so that efficient data management can be planned before the execution.
Letting an implementation know a sequence of functions that we want to
call on an input image will allow it to make an informed decision on where
to keep image data and when to move it to/from an accelerator. A graph
model is used in OpenVX to represent this information.
The idea of using graphs to express sequences of image processing
functions has been around for a long time (see, e.g., [16]). A program is
represented by a Directed Acyclic Graph (DAG), with nodes correspond-
ing to functions and oriented links between nodes setting the input and
output data, as well as the order of execution. An example of a graph is
given in Fig. 1.1. The input color image in the RGB format is converted
into the YUV format by the first (leftmost) node, then a Y (intensity) chan-
nel is extracted by the second node, and, finally, the camera lens distortion
correction is done on the Y channel. If a function has parameters, then
they are specified by node attributes, similar to data object attributes.
An OpenVX graph is set up in advance, before execution. Once all the
nodes and links are set up, the graph is verified, either automatically before
execution or with a manual function call. During graph verification time,
the OpenVX implementation may prepare for execution, make decisions
about which accelerator to use for executing each node, and check for er-
rors in the graph. For some platforms such as FPGA, it can also compile a
graph into binary code and upload it to an FPGA chip. Once the verifi-
cation is complete, the graph is ready to be executed. Note that the same
graph can be executed multiple times on different data without reverifica-
tion. Changes in a graph such as insertion/deletion of nodes or links can
trigger reverification. Changes in some node attributes may also require
reverification before the next execution. A list of OpenVX graph nodes
representing computer vision functions, along with the references to spe-
8 OpenVX Programming Guide
Figure 1.2 An example of an OpenVX graph with virtual images. Graph links with vir-
tual images define a sequence of calls, but a user has no access to that data at any time.
cific chapters where these functions are discussed and to the code samples,
is given in Appendix B.
Obviously, not every possible computer vision function can be prede-
fined by OpenVX. Users can implement a C function and add it to a graph
as a user node. A user node will be executed on a CPU, but it does not
prevent using accelerators for user nodes. For instance, Chapter 13 dis-
cusses how a user node can be implemented with OpenCL, so that code
get executed on a GPU/FPGA/DSP. The concept of user nodes allows a
developer to go well beyond the set of computer vision functions that are
included in the OpenVX standard.
1.5 Virtual objects
To provide an OpenVX implementation more opportunities for optimiza-
tion, a user has no access to data during graph execution (other than from
user nodes). In the example of Fig. 1.1, once the graph execution is fin-
ished, access to all four images (input image, image 1, image 2, and output
image) is possible. However, in many cases a user does not need to access
temporary data (images 1 and 2), whereas generating it may cause an addi-
tional speed and memory overhead. For example, if we did not need images
1 and 2, all three operations could happen in place, pixel by pixel, provid-
ing a lot of speedup on platforms with slow memory bus by minimizing
the data flow. Examples of such operations include combining subsequent
per-pixel image arithmetic operations and applying several linear filters to
an image at the same time (so-called filter stacking). If a user does not
need specific images, then he can create them as virtual (see Fig. 1.2). Vir-
tual images, like regular data objects, connect two graph nodes, but an
implementation is not obliged to actually generate data for these images.
OpenVX does not require a user to provide image dimensions when cre-
ating a virtual image; zeroes can be used instead (see vxCreateVirtualImage
Introduction 9
in Chapter 6). It may be useful when you have a large graph with many
images of different sizes (e.g., when using pyramids, see Chapter 8), but
the authors recommend to use actual image sizes where possible, as it is less
error prone.
All other OpenVX data objects, such as pyramids and histograms, can
also be virtual. It is reasonable to expect that Map/Unmap function calls
on virtual objects fail, but OpenVX requires this only outside of graph
execution. If Map/Unmap are called inside a graph (from a user node),
then the calls will be successful. So, a user of OpenVX has to be aware that
inserting a user node in a sequence of nodes connected with virtual images
may prevent certain optimizations.
1.6 Deep neural networks
Deep neural networks have recently become the standard tool for solving
a variety of computer vision problems. Whereas training a neural network
is outside the OpenVX scope, importing a pretrained network and run-
ning inference on it is an important part of the OpenVX functionality. The
concept of the Graph API of nodes representing functions and links rep-
resenting data is very convenient for implementing deep neural networks
with OpenVX. In fact, each neural network unit can be represented as a
graph node. OpenVX has a special data type representing tensors to pro-
vide data exchange between these nodes, and the nodes themselves are
implemented in the OpenVX Neural Network Extension [17]. Another way
to import a neural network into OpenVX is by using the OpenVX Kernel
Import Extension [18]. The Kernel Import Extension can take a pretrained
network model and load it into OpenVX as a single node. One of the data
formats that can be used is Neural Network Exchange Format (NNEF) [20],
the standard also developed by the Khronos Group. See Chapter 10 for
details on how to import a pretrained neural network into OpenVX.
1.7 Immediate mode API
There are cases where the Graph API in its existing form does not provide
an efficient implementation of a computer vision algorithm. For example,
a sequence of quick custom operations on small blocks of pixels intermit-
tent with standard computer vision functions, defined with a Graph API,
will require a user node for each custom operation. Each user node will
have to map and unmap an image to/from host memory, causing a signif-
10 OpenVX Programming Guide
icant overhead. This is why OpenVX, on top of the Graph API, also has
the Immediate Mode API. Each immediate mode function replicates a graph
node, with a few exceptions. The behavior of an immediate mode function
is defined by the corresponding single node graph. Each immediate mode
function name is prefixed with “vxu”, to distinguish them from Graph API
functions, prefixed with “vx”. An immediate mode API allows us to write
a computer vision algorithm without a graph and has small overhead for
custom data processing.
1.8 OpenVX vs. OpenCV and OpenCL
OpenVX is not the first framework optimized for computer vision.
OpenCV (Open Source Computer Vision Library) [21] is the de facto
standard in this area. It provides a lot of useful functions, and many of them
are optimized for specific platforms, including x86, ARM CPUs, and se-
lected GPUs. Also, OpenCL [12], a generic programming framework, can
be used to develop computer vision algorithms that can run on discrete
GPUs. So why do we need another computer vision platform?
OpenCV is developed and maintained by the community, with the pri-
mary focus on providing necessary computer vision functionality. Many
functions are optimized for a subset of platforms, and hardware vendors
provide their own optimizations for their platforms (see, e.g., [22–24]).
However, the wide scope of the library makes the optimization of all
the functions for multiple embedded platforms next to impossible. Also,
OpenCV uses algorithmic tests to check for function correctness, so the
results across platforms may be different (although functionally correct). In
contrast, OpenVX has a much smaller scope, making it possible to optimize
for a wide range of platforms. Also, OpenVX conformance tests make sure
the results to be similar across different platforms. In fact, these frameworks
are complimentary: we can use the OpenVX graph for functions that are
defined by OpenVX and then add more functionality as user nodes, based
on OpenCV. A full comparison between OpenCV and OpenVX is given
in the Table 1.1.
OpenCL is a general-purpose programming language that allows us to
write code for heterogeneous systems. OpenCL existing requirement for
full IEEE 754 floating point standard compliance2 and its explicit memory
2 This requirement may be relaxed in future.
Introduction 11
Table 1.1 OpenVX vs. OpenCV∗.
OpenCV OpenVX
Implementation Community-driven
open-source library
Callable API implemented,
optimized, and shipped by
hardware vendors
Scope 100s of imaging and vision
functions, multiple camera
APIs/interfaces
Tight focus on dozens of
core hardware-accelerated
functions plus extensions and
accelerated custom nodes
uses external camera drivers
Conformance Extensive OpenCV Test
Suite but no formal Adopters
program
Implementations must pass
Khronos Conformance Test
Suite to use trademark
IP Protection None. Source code licensed
under BSD. Some modules
require royalties/licensing
Protected under Khronos IP
Framework – Khronos
members agree not to assert
patents against API when
used in Conformant
implementations
Acceleration OpenCV 3.0 Transparent
API (or T-API) enables
function offload to OpenCL
devices
Implementation free to use
any underlying API such as
OpenCL. Can use OpenCL
for Custom Nodes
Efficiency OpenCV 4.0 G-API graph
model for some filters,
arithmetic/binary operations,
and well-defined geometrical
transformations
Graph-based execution of all
Nodes. Optimizable
computation and data transfer
Inferencing Deep Neural Network
module to construct
networks from layers for
forward pass computations
only. Import from ONNX,
TensorFlow, Torch, Caffe
Neural Network layers and
operations represented
directly in the OpenVX
Graph. NNEF direct import,
ONNX through NNEF
convertor
∗ Courtesy of Neil Trevett, Khronos Group
model prevent OpenVX to be implemented only using OpenCL. However,
OpenCL can be used to efficiently implement a user node to run on a
GPU (see Chapter 13 for more information). Also, OpenCV T-API can
be used to run a computer vision algorithm implemented in OpenVX on
an accelerator through OpenCL. A full comparison between OpenCL and
OpenVX is given in the Table 1.2.
12 OpenVX Programming Guide
Table 1.2 OpenVX vs. OpenCL∗.
OpenCL OpenVX
Use case General heterogeneous
programming
Domain targeted vision
processing
Ease of use General-purpose math
libraries with no built-in
vision functions
Fully implemented vision
operators and framework
“out of the box”
Architecture Language-based – needs
online compilation
Library-based – no online
compiler required
Target hardware “Exposed” architected
memory model – can impact
performance portability
Abstracted node and memory
model – diverse
implementations can be
optimized for power and
performance
Precision Full IEEE floating point
mandated
Minimal floating point
requirements – optimized for
vision operators
∗ Courtesy of Neil Trevett, Khronos Group
1.9 OpenVX versions
This revision of the book was mostly written for OpenVX version 1.2.
Version 1.3 was released in September 2019, and Chapter 15 provides in-
formation on the new features introduced in OpenVX 1.3.
1.10 Prerequisites
We expect a reader to be familiar with the C programming language, as
the API and all of the examples are in C. Also, we assume that a reader
has experience with computer vision, and so we will not go into explana-
tions about what a specific algorithm does or how it works. Here are some
useful references on the subject [13, 25, 26, 27]. Having experience with
OpenCV [21,28] can also be useful.
1.11 Code samples
Throughout the book, we often make use of code samples that show how to
work with specific OpenVX functions. The samples are available for down-
load from https://github.com/rgiduthuri/openvx_tutorial [29] under MIT
license. The input data for them is located at https://www.dropbox.com/
sh/urzzs64a85tqf3d/AADxdIEer_tjHFGzBifZWhsLa. The samples have
Introduction 13
been tested against the OpenVX sample implementation [30] available
for download from https://github.com/KhronosGroup/OpenVX-sample-
impl. See the beginning of Chapter 2 for the details on how to build and
run the samples.
CHAPTER 2
Build your first OpenVX program
Contents
2.1. First things first 15
2.1.1 How to get the OpenVX sample implementation 16
2.1.2 Building the examples 16
2.2. Immediate mode 17
2.2.1 Our first very simple example in immediate mode 17
2.3. Graph mode 22
2.3.1 Creating the graph 22
2.3.2 Connecting to parameters and running the graph 24
2.3.3 Running example 4 25
2.3.4 Code for example 4 25
2.4. Image input and output 30
2.4.1 Converting to and from portable pixmap format 30
2.4.2 Other libraries for input and output of images 31
2.4.3 Image processing exercises 31
2.4.4 Answers, or at least hints, for the exercises 32
2.1 First things first
If you are going to build an OpenVX program, you will need a few things:
• A computer. In this first chapter, we assume that you are using a Linux-
based machine; in general, this is the easiest option, and what you read
in this chapter has been tested on a machine running Ubuntu 16.04.
• A text editor to write your program. You probably have a favorite,
whether it is something simple like Vi, Nano, or Scite, or more com-
plex like Emacs or Eclipse.
• A toolchain. For the examples, we assume GCC and give code in C99
(ISO/IEC 9899:1999, ref TBD). This should be a suitably low com-
mon denominator. You will also need CMake to build the examples.
• Some examples depend on 3rd party libraries: OpenCV [21], vxa [31],
OpenVX-SC-Plus [32], and LAPACK [33].
• Our examples, downloaded from https://github.com/rgiduthuri/
openvx_tutorial [29].
• Input data for the examples, downloaded from https://www.dropbox.
com/sh/urzzs64a85tqf3d/AADxdIEer_tjHFGzBifZWhsLa.
OpenVX Programming Guide
https://doi.org/10.1016/B978-0-12-816425-9.00008-5
Copyright © 2020 Elsevier Inc.
All rights reserved. 15
16 OpenVX Programming Guide
• Last but not least, a copy of OpenVX. We use the sample implementa-
tion from Khronos, but of course you may have another implementa-
tion from your favorite IP supplier.
2.1.1 How to get the OpenVX sample implementation
Both the OpenVX Specification and a sample implementation may be
obtained from https://www.khronos.org/registry/OpenVX/. The sample
implementation of OpenVX V1.2 is a tar.bz2 file, which should be unar-
chived to a suitable folder, for example, your home folder, and then
built using the instructions in the file “README”. If you opt to install
OpenVX on a Linux system, then the libraries will be available in /us-
r/lib/, and the include file path for building the examples in this book will
be ˜/OpenVX_sample/include, assuming that you unzipped the archive
into your home folder.
2.1.2 Building the examples
Assuming that you unzipped the sample implementation into your home
folder, then built and installed it, the following commands should be suffi-
cient to build the examples for this chapter. Note that the first three exam-
ples need the immediate-mode library (vxu) in addition to the OpenVX
library.
$ mkdir build  cd build
$ gcc ../book_sample/example1/example1.c -I ~/OpenVX_sample/include -l
OpenVX -l vxu -o example1
$ gcc ../book_sample/example2/example2.c -I ~/OpenVX_sample/include -l
OpenVX -l vxu -o example2
$ gcc ../book_sample/example3/example3.c -I ~/OpenVX_sample/include -l
OpenVX -l vxu -o example3
$ gcc ../book_sample/example4/example4.c -I ~/OpenVX_sample/include -l
OpenVX -o example4
$ gcc ../book_sample/example4/example4a.c
../book_samples/ppm-io/writeImage.c -I ~/OpenVX_sample/include -I
../book_samples/ppm-io/ -l OpenVX -o example4a
$ gcc ../book_sample/example4/changeImage.c
../book_samples/ppm-io/readImage.c
../book_samples/ppm-io/writeImage.c -I ~/OpenVX_sample/include -I
../book_samples/ppm-io/ -l OpenVX -o changeImage
To build all examples, you will need to first install the prerequisites
(OpenCV, vxa, and LAPACK) and then use CMake to build the examples.
Build your first OpenVX program 17
The following commands assume that your current directory is the folder
“book_samples” with the samples:
$ cd ../
$ mkdir build
$ cd build
$ cmake -G Unix Makefiles ../book_samples
$ make
2.2 Immediate mode
There are two ways to use OpenVX, “immediate mode” and “graph
mode”. The immediate mode is pretty straightforward, and is there re-
ally only for quick prototyping with the OpenVX functions, so that people
who are used to using traditional APIs like OpenCV can get a quick flavor
of the sort of functionality that is available in OpenVX. But the real power
is in the “graph mode”, because this allows implementations to do a lot of
optimization. The graph mode is intended to address the main use case of
OpenVX, which is when you want to apply the same processing repeatedly
to different data, for example, a camera feed.
The immediate mode does what you might expect—you call a function
and immediately get the results. For example, you can do a pixel-by-pixel
multiplication of two images and get the result in a third image simply by
calling one function. We will use the immediate mode for our first example.
2.2.1 Our first very simple example in immediate mode
The code for this example is in example1/example1.c. In outline, what it
does is:
• Create an OpenVX context
• Create an image that is a white rectangle on a black background
• Locate the corners in the image, using the Fast Corners algorithm with
and without nonmaximum suppression
• Display the results
The first thing we have in the main() function is the line
vx_context context = vxCreateContext();
This line creates the OpenVX context, which is the first thing we have to
do in any OpenVX program. Without this, there is nothing else we can do
18 OpenVX Programming Guide
with OpenVX. Similarly, when we have finished with OpenVX, we release
the context—in our example, this is at the end of main():
vxReleaseContext(context);
This tells the OpenVX infrastructure that we have finished with the con-
text, and it can go ahead and destroy it, together with anything else inside
it that we have not already destroyed.
The second thing we do after calling vxCreateContext is checking that
the context is OK and there were no any errors. In general, you can check
any type of vx_reference by calling the function vxGetStatus() on it; if the
result is VX_SUCCESS then it is OK to use it in further functions. If the result
is not equal to this value, then probably it is a good time to stop. We have
made a useful function errorCheck() that bails out with exit(1) if the status
is nonzero—note that VX_SUCCESS is defined as being zero:
void errorCheck(vx_context *context_p, vx_status status, const char
*message)
{
if (status)
{
puts(ERROR! );
puts(message);
vxReleaseContext(context_p);
exit(1);
}
}
We use this in the main function right after creating the context:
errorCheck(context, vxGetStatus((vx_reference)context), Could not
create a vx_contextn);
Let us take a look at the second function we have defined to create our
white rectangle on a black background:
vx_image makeInputImage(vx_context context)
{
vx_image image = vxCreateImage(context, 1024U, 768U, VX_DF_IMAGE_U8);
vx_rectangle_t rect = {
.start_x = 256, .start_y = 192, .end_x=768, .end_y = 576
};
Build your first OpenVX program 19
if (VX_SUCCESS == vxGetStatus((vx_reference)image))
{
vx_image roi = vxCreateImageFromROI(image, rect);
vx_pixel_value_t pixel_white, pixel_black;
pixel_white.U8 = 255;
pixel_black.U8 = 0;
if (VX_SUCCESS == vxGetStatus((vx_reference)roi) 
VX_SUCCESS == vxSetImagePixelValues(image, pixel_black) 
VX_SUCCESS == vxSetImagePixelValues(roi, pixel_white))
vxReleaseImage(roi);
else
vxReleaseImage(image);
}
return image;
}
We make our black background by filling all of the image and then a white
rectangle by filling an ROI (region of interest) in this background. When
we have finished with the ROI, we release it as we need not it any more.
Note that we do not touch the image or the ROI further if we get an error
on creating them, and if we fail to create the ROI or copy the data, then
we release the image and return a NULL pointer.
Back in the main() function, we have to test the image after we have
created it:
vx_image image1 = makeInputImage(context);
errorCheck(context, vxGetStatus((vx_reference)image1), Could not
create image);
Now we still need some other parameter declarations before we can
call the vxu functions to find the corners. You will find descriptions of
the parameters for vxuFastCorners in the documentation for OpenVX. We
also allocate some host memory for an array of key points as we will need
somewhere to get our results back from OpenVX so that we can display
them:
vx_float32 strength_thresh_value = 128.0;
vx_scalar strength_thresh = vxCreateScalar(context, VX_TYPE_FLOAT32,
strength_thresh_value);
vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
vx_array corners1 = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
vx_size num_corners_value = 0;
20 OpenVX Programming Guide
vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE,
num_corners_value);
vx_scalar num_corners1 = vxCreateScalar(context, VX_TYPE_SIZE,
num_corners_value);
vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t));
Now we have to check that everything has been created OK, including
kp, which should not be NULL:
errorCheck(context,
kp == NULL ||
vxGetStatus((vx_reference)strength_thresh) ||
vxGetStatus((vx_reference)corners) ||
vxGetStatus((vx_reference)num_corners) ||
vxGetStatus((vx_reference)corners1) ||
vxGetStatus((vx_reference)num_corners1),
Could not create parameters for FastCorners);
Then we can call the functions to do the actual work, once with non-
maximum suppression and once without:
errorCheck(context, vxuFastCorners(context, image1, strength_thresh,
vx_true_e, corners, num_corners), Fast Corners function failed);
errorCheck(context, vxuFastCorners(context, image1, strength_thresh,
vx_false_e, corners1, num_corners1), Fast Corners function
failed);
The last part of the example reads the output arrays and shows the results.
You can clearly see the effect of nonmaximum suppression; in the following
examples, we will use nonmaximum suppression, so that we see just the
corners we expect.
In example 2, we use vxuWarpAffine to rotate our input image through
90 degrees and then vxuOr to logically OR the two images together, creating
a white cross on a black background, and of course we see 12 corners. The
affine transform is a rotation where each output x and y pixel is taken from
pixels at addresses given by a linear combination of the input addresses; if the
input address is not integral, then interpolation is performed as specified.
The coefficients given in the code as the matrix_values simply swap x and
y with no translation (no offset); then this is a 90-degree rotation.
Extracts from example2/example2.c:
vx_matrix warp_matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32, 2U,
3U);
Build your first OpenVX program 21
vx_float32 matrix_values[3][2] = { /* Rotate through 90 degrees */
{0.0, 1.0}, /* x coefficients */
{1.0, 0.0}, /* y coefficients */
{0.0, 0.0} /* offsets */
};
errorCheck(context, vxCopyMatrix(warp_matrix, matrix_values,
VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST), Could not initialize the
matrix);
errorCheck(context, /* Now image2 set to image 1 rotated */
vxuWarpAffine(context, image1, warp_matrix,
VX_INTERPOLATION_NEAREST_NEIGHBOR, image2) ||
/* image3 set to logical OR of images 1 and 2 */
vxuOr(context, image1, image2, image3) ||
/*And now count the corners */
vxuFastCorners(context, image3, strength_thresh, vx_true_e,
corners, num_corners),
Image functions failed);
In example 3, we take this further, using the Sobel, Magnitude, and Di-
late filters to replace the white cross with the outline of a white cross, and
the corner count doubles as both internal and external corners are counted.
The Sobel filter is an edge detection filter, and applying it to the cross gives
us two output images, the gradient in the x direction and the gradient in
the y direction. Taking the magnitude of the x and y images gives us a solid
line of single pixels outlining the cross. Note that the vxuMagnitude function
takes two 16-bit signed images that are the output of vxuSobel and produces
a 16-bit signed image as output. However, most other OpenVX functions
only operate on 8-bit unsigned images, so we use the vxuConvertDepth func-
tion to produce such an image, with VX_CONVERT_POLICY_SATURATE specifying
that values below zero should be set to zero, and values above 255 should be
set to 255. Finally, the vxuDilate3x3 function applies a dilation filter, mak-
ing the thin outline more substantial. So far, we have imagined how the
output will actually look, but if you would like a sneak preview, then look
at Fig. 2.2 later in the chapter.
From example3/example3.c:
/* Now image2 set to image 1 rotated */
vx_status status = vxuWarpAffine(context, image1, warp_matrix,
VX_INTERPOLATION_NEAREST_NEIGHBOR, image2);
/* image3 set to logical OR of images 1 and 2 and then processed as
described above */
22 OpenVX Programming Guide
errorCheck(context, vxuOr(context, image1, image2, image3) ||
vxuSobel3x3(context, image3, grad_x, grad_y) ||
vxuMagnitude(context, grad_x, grad_y, magnitude) ||
vxuConvertDepth(context, magnitude, converted,
VX_CONVERT_POLICY_SATURATE, 1) ||
vxuDilate3x3(context, converted, dilated) ||
/* And now count the corners */
vxuFastCorners(context, dilated, strength_thresh, vx_true_e, corners,
num_corners),
Image functions failed);
In example 3, you see that we are using a lot of images! In fact, most
of these images really need not be retained, since we are not interested in
the intermediate data. The only image of any interest is actually the input
image that we create. After that, we only use the output from the final
corner detector.
2.3 Graph mode
Let us now have a look at graph mode. Example 4 is a graph-mode version
of example 3, and comparing the two will help us see how it works. To
make it easier to understand the various things going on, example 4 (see
the file example4/example4.c) introduces a number of changes:
• The function makeInputImage() now takes two more parameters, the
half-width and half-height of the rectangle in the image, so it is easy to
make different images.
• A new function makeTestGraph() creates a graph that implements the
functionality of the vxu functions that were called in main() in exam-
ple 3.
• Similarly, a new function showResults() runs the graph with an image
and displays the results.
• A new function getGraphParameter() is a simple wrapper to make some
of the OpenVX library functions more accessible.
• The new main() is a lot simpler, and it runs the graph with two different
images.
Now let us look at things in more detail.
2.3.1 Creating the graph
In the function makeTestGraph(), we make extensive use of virtual images—
these are images where we do not want either to read or write the data, and
Build your first OpenVX program 23
we do not care really how the framework decides to implement them. We
use them to connect together processing nodes in the graph, just to inform
the framework about the topology. Note that is does not matter in which
order nodes are added to the graph (here we do it backward), because the
topology is totally defined by the connecting edges, that is, the data objects.
The graph constructed in makeTestGraph() can be visualized as in
Fig. 2.1. For clarity, some parameters (edges) of the graph are excluded,
but all the processing nodes are there. As you can see, there is opportunity
for parallel execution. Since the data objects are virtual, the implementation
is free to perform optimization by merging nodes together.
Figure 2.1 Illustration of the graph created in example 4. Graph nodes represent func-
tions, and the directed links define input and output data.
The input image we create is not virtual, but like the virtual images and
the other interim objects, and even those objects containing the output
data, we release our reference to it before exiting the function. How then
do we introduce an input or see the results?
The answer is that we use graph parameters. Graph parameters in
OpenVX give you a simple mechanism for changing input or output ob-
jects in a graph once it has been made, and it is a mechanism that allows
the actual content of the graph (the nodes and edges) to remain hidden.
A graph parameter is associated with the input or output of one node only,
so if you want an object to be an input for several nodes, then you can use
a Copy node to isolate the input graph parameter. Copy nodes simply copy
their input to their output; this is of course a logical or conceptual opera-
tion, and the implementation will just “optimize away” the Copy node if
its output is a virtual object, so there is no actual overhead when executing
the graph.
Note that our input image in example 3 was an input to two vxu func-
tions, vxuWarpAffine() and vxuOr(), and similarly when we create a graph
24 OpenVX Programming Guide
implementing this same functionality, we feed the same input image into
two nodes. It is inconvenient to parameterize two images in this way; we
would like just the one input to go to two places. So we introduce the
useful Copy node; the output of this feeds both vxWarpAffineNode() and
vxOrNode(), and we are able to create just one graph parameter associated
with the input of the Copy node:
vxAddParameterToGraph(graph, vxGetParameterByIndex(vxCopyNode(graph,
(vx_reference)input, (vx_reference)imagesU8[0]), 0));
It is important to note that graph parameters are added in order; since we
added this one first, it will be identified with an index value of zero, and
the output parameters that we add afterward will have indices 1 and 2. The
assumption is that the creator of the graph will document it correctly so
that anyone using it will know which parameter is which.
Note that we do not have much error checking in the graph creation
function. This is because errors in creation of all the objects and nodes will
be caught when the graph is processed; if we need more detail, then we
can turn on logging and see some diagnostic output from the framework
without having to test each function call result.
2.3.2 Connecting to parameters and running the graph
So, have a look at main() in the file example4/example4.c; it is also re-
produced later in this section. Here we create the graph by calling the
makeTestGraph() function and a couple of images, just rectangles of differing
sizes, so we can illustrate calling the graph with a different input parame-
ter. Running the graph and displaying the results is the work of our new
function showResults(), which takes a vx_graph and a vx_image as parameters.
It sets the image as the input parameter to the graph, which as you will
remember was given the index 0 by adding it to the graph first:
vxSetGraphParameterByIndex(graph, 0, (vx_reference)image);
Next, we run the graph using the function vxProcessGraph(). The first time
it is run, vxVerifyGraph() will be called internally, and this is the point at
which any errors will be picked up. At this point the implementation may
do any other processing it deems necessary. Some implementations may
aggressively optimize the graph, taking advantage of any virtual objects so
that kernels can be coalesced and data movement reduced to a minimum.
At the present time the Khronos Sample Implementation does not do this,
but implementations from hardware vendors will do so.
Build your first OpenVX program 25
The second and third graph parameters are the array of corners and
number of corners. These are extracted from the graph using our handy
function getGraphParameter() and then processed just as in the previous ex-
ample 3. It is worth having a little look at getGraphParameter() as it illustrates
a use of one of the vxQueryXXX() functions.
2.3.3 Running example 4
Example 4 gives two sets of output. The first should be the same as for
example 3, that is, 24 corners, as the image is identical. The second set of
results should give just 8 corners, since the input image is a square, which
when rotated 90 degrees and superimposed upon itself is still a square, so
you will see 8 corners, 4 external and 4 internal.
Figure 2.2 The output images from example 4a. The images were created using the
same graph with different input images.
2.3.4 Code for example 4
void errorCheck(vx_context *context_p, vx_status status, const char
*message)
{
if (status)
{
puts(ERROR! );
puts(message);
vxReleaseContext(context_p);
exit(1);
}
}
vx_image makeInputImage(vx_context context, vx_uint32 width, vx_uint32
height)
26 OpenVX Programming Guide
{
vx_image image = vxCreateImage(context, 100U, 100U,
VX_DF_IMAGE_U8);
if (width  48)
width = 48;
if (height  48)
height = 48;
vx_rectangle_t rect = {
.start_x = 50 - width, .start_y = 50 - height, .end_x = 50 +
width, .end_y = 50 + height
};
if (VX_SUCCESS == vxGetStatus((vx_reference)image))
{
vx_image roi = vxCreateImageFromROI(image, rect);
vx_pixel_value_t pixel_white, pixel_black;
pixel_white.U8 = 255;
pixel_black.U8 = 0;
if (VX_SUCCESS == vxGetStatus((vx_reference)roi) 
VX_SUCCESS == vxSetImagePixelValues(image, pixel_black) 
VX_SUCCESS == vxSetImagePixelValues(roi, pixel_white))
vxReleaseImage(roi);
else
vxReleaseImage(image);
}
return image;
}
vx_graph makeTestGraph(vx_context context)
{
vx_graph graph = vxCreateGraph(context);
int i;
vx_image imagesU8[5], imagesS16[3];
vx_image input = vxCreateImage(context, 100U, 100U,
VX_DF_IMAGE_U8);
for (i = 0; i  5; ++i)
imagesU8[i] = vxCreateVirtualImage(graph, 100, 100,
VX_DF_IMAGE_U8);
for (i = 0; i  3; ++i)
imagesS16[i] = vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_VIRT);
Build your first OpenVX program 27
vx_matrix warp_matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32,
2U, 3U);
vx_float32 matrix_values[6] = {0.0, 1.0, 1.0, 0.0, 0.0, 0.0 }; /*
Rotate through 90 degrees */
vx_float32 strength_thresh_value = 128.0;
vx_scalar strength_thresh = vxCreateScalar(context,
VX_TYPE_FLOAT32, strength_thresh_value);
vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100);
vx_size num_corners_value = 0;
vx_int32 shift_value = 1;
vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE,
num_corners_value);
vx_scalar shift = vxCreateScalar(context, VX_TYPE_INT32,
shift_value);
vxCopyMatrix(warp_matrix, matrix_values, VX_WRITE_ONLY,
VX_MEMORY_TYPE_HOST);
/* Create the nodes to do the processing, order of creation is not
important */
vx_node last_node = vxFastCornersNode(graph, imagesU8[4],
strength_thresh, vx_true_e, corners, num_corners);
vxDilate3x3Node(graph, imagesU8[3], imagesU8[4]);
vxConvertDepthNode(graph, imagesS16[2], imagesU8[3],
VX_CONVERT_POLICY_SATURATE, shift);
vxMagnitudeNode(graph, imagesS16[0], imagesS16[1], imagesS16[2]);
vxSobel3x3Node(graph, imagesU8[2], imagesS16[0], imagesS16[1]);
vxOrNode(graph, imagesU8[0], imagesU8[1], imagesU8[2]);
vxWarpAffineNode(graph, imagesU8[0], warp_matrix,
VX_INTERPOLATION_NEAREST_NEIGHBOR, imagesU8[1]);
/* Setup input parameter using a Copy node */
vxAddParameterToGraph(graph,
vxGetParameterByIndex(vxCopyNode(graph, (vx_reference)input,
(vx_reference)imagesU8[0]), 0));
/* Setup the output parameters from the last node */
vxAddParameterToGraph(graph, vxGetParameterByIndex(last_node, 3));
/* array of corners */
vxAddParameterToGraph(graph, vxGetParameterByIndex(last_node, 4));
/* number of corners */
28 OpenVX Programming Guide
/* Release resources */
vxReleaseImage(input);
for (i = 0; i  5; ++i)
vxReleaseImage(imagesU8[i]);
for (i = 0; i  3; ++i)
vxReleaseImage(imagesS16[i]);
vxReleaseMatrix(warp_matrix);
vxReleaseScalar(strength_thresh);
vxReleaseScalar(num_corners);
vxReleaseScalar(shift);
vxReleaseArray(corners);
return graph;
}
vx_reference getGraphParameter(vx_graph graph, vx_uint32 index)
{
vx_parameter p = vxGetGraphParameterByIndex(graph, index);
vx_reference ref = NULL;
vxQueryParameter(p, VX_PARAMETER_REF, ref, sizeof(ref));
vxReleaseParameter(p);
return ref;
}
void showResults(vx_graph graph, vx_image image, const char * message)
{
vx_context context = vxGetContext((vx_reference)graph);
puts(message);
vxSetGraphParameterByIndex(graph, 0, (vx_reference)image);
if (VX_SUCCESS == vxProcessGraph(graph))
{
vx_size num_corners_value = 0;
vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t));
errorCheck(context,
vxCopyScalar((vx_scalar)getGraphParameter(graph, 2),
num_corners_value,
VX_READ_ONLY, VX_MEMORY_TYPE_HOST), vxCopyScalar failed);
printf(Found %zu corners with non-max suppressionn,
num_corners_value);
/* Array can only hold 100 values */
if (num_corners_value  100)
Build your first OpenVX program 29
num_corners_value = 100;
errorCheck(context,
vxCopyArrayRange((vx_array)getGraphParameter(graph, 1),
0,
num_corners_value, sizeof(vx_keypoint_t), kp,
VX_READ_ONLY, VX_MEMORY_TYPE_HOST), vxCopyArrayRange
failed);
for (int i=0; inum_corners_value; ++i)
{
printf(Entry %3d: x = %d, y = %dn, i, kp[i].x,
kp[i].y);
}
free(kp);
}
else
{
printf(Graph processing failed!);
}
}
int main(void)
{
vx_context context = vxCreateContext();
errorCheck(context, vxGetStatus((vx_reference)context), Could
not create a vx_contextn);
vx_graph graph = makeTestGraph(context);
vx_image image1 = makeInputImage(context, 30, 10);
vx_image image2 = makeInputImage(context, 25, 25);
showResults(graph, image1, Results for Image 1);
showResults(graph, image2, Results for Image 2);
vxReleaseContext(context);
return 0;
}
30 OpenVX Programming Guide
2.4 Image input and output
In the example code, a simple library is included, which provides input and
output of portable pixelmap (.ppm) and portable greyscale map (.pgm) files.
This may be found in the files ppm-io/readImage.h, ppm-io/readImage.c,
ppm-io/writeImage.h, and ppm-io/writeImage.c, for you to statically link
with the examples. You can see these in action; example4/example4a.c is
just the same as example4/example4.c except that it adds another output
parameter to the graph, the image that has its corners counted, and writes
this to a .pgm file so that you can see the image created.
The main() function looks like this:
int main(void)
{
vx_context context = vxCreateContext();
errorCheck(context, vxGetStatus((vx_reference)context), Could not
create a vx_contextn);
vx_graph graph = makeTestGraph(context);
vx_image image1 = makeInputImage(context, 30, 10);
vx_image image2 = makeInputImage(context, 25, 25);
showResults(graph, image1, Results for Image 1);
writeImage((vx_image)getGraphParameter(graph, 3), example4-1.pgm);
showResults(graph, image2, Results for Image 2);
writeImage((vx_image)getGraphParameter(graph, 3), example4-2.pgm);
vxReleaseContext(context);
return 0;
}
2.4.1 Converting to and from portable pixmap format
Portable pixmaps may be viewed under Linux using standard image viewers
and on Windows using “Irfanview”. There are a variety of tools that may
be used to convert other formats to portable pixmap format, for example,
on Linux, you can convert a portable pixmap to jpeg simply by
$ convert image.ppm image.jpg
Similarly, a conversion the other way is just as easy:
$ convert image.jpg image.ppm
Build your first OpenVX program 31
The “convert” command is part of the “imagemagick” package and sup-
ports very many formats. If it is not on your system, then try the following
command:
$ sudo apt-get install imagemagick
On Windows, Irfanview can read and write a wide variety of image for-
mats, and after opening in one format, an image may be saved in another
format. This conversion may also be done on the command line using the
“/convert” option. For more information, see www.irfanview.com.
2.4.2 Other libraries for input and output of images
There are many other libraries useful for reading and writing images, per-
haps most notably OpenCV. Other useful and less heavyweight libraries
are libpng (see http://www.libpng.org/pub/png/libpng.html), libjpeg (see
http://libjpeg.sourceforge.net/), and of course imagemagick; see http://
www.imagemagick.org.
2.4.3 Image processing exercises
Another example, “example4/changeImage.c”, is a framework that reads
in an RGB (.ppm) image and processes it before outputting it again. It uses
a Canny edge detector to find edges in the intensity and adds these back
in to the image as black outlines. The graph is constructed in the function
“makeTestGraph”, and the processing follows the flow shown in Fig. 2.3.
Here we are using the Canny Edge Detect filter to find edges in an
image. This is a more complex and more sophisticated algorithm than the
method we used to detect edges in the previous examples (i.e., the Sobel
filter followed by magnitude calculation) and is more suitable for use with
greyscale images. A description of the Canny detector (and many other
things!) may be found on the University of Edinburgh School of Informat-
ics homepages [34].
The image we are operating on is the Y (luma) channel of an image that
has been converted from RGB. We recombine the Y, U, and V planes back
into a single image before converting it back to RGB to output again. An
example of the results are shown in Fig. 2.4.
We leave it for the reader to experiment:
1. What are the effects of changing the hysteresis values in the Canny
edge detector?
2. Why use the intensity for finding edges—what about changes in color?
32 OpenVX Programming Guide
Figure 2.3 Visualization of the graph in the “changeImage” example. Graph nodes rep-
resent functions, and the directed links define input and output data.
3. I want thicker lines in my outlines.
4. I want to recolor this using just five colors.
5. I want a color-free “halo” around the lines.
6. I want to run this graph on several different images.
Figure 2.4 Example of input and output images for the “changeImage” example. An ex-
ample image (left) and corresponding output with edges added (right).
2.4.4 Answers, or at least hints, for the exercises
1. Setting the upper tracking threshold too low increases the number of
undesirable edge fragments appearing in the output; setting the lower
tracking threshold too high will cause noisy edges to break up, so set
Build your first OpenVX program 33
upper_value quite high and lower_value quite low; the original values
are not the best!
2. Try using the other extracted channels (U, V, or both) to find edges
and then place them on the Y plane as black lines.
3. Dilate the lines before adding them.
4. Use a look-up table or two convert depth operations back-to-back to
reduce the number of colors.
5. Create two sets of lines, one dilated more than the other. The wider
ones can be used to remove color and increase intensity on the Y plane
before ANDing the thinner ones as black lines.
6. Add parameters to the graph—you will need no any Copy nodes as the
input image goes only to one node.
CHAPTER 3
Building an OpenVX graph
Contents
3.1. Linking nodes 36
3.2. Virtual images 37
3.3. Graph verification and execution 39
3.4. Parameter validation 43
3.5. What can be changed at runtime? 44
3.6. Example 46
To build an OpenVX graph, we start by creating all the data objects we
will use in the graph. Once all the data objects we need are created, we
can create the graph object itself and all the nodes that will do the actual
computations using our data objects as parameters to the node-creation
functions. As we create the nodes, we insert them into the graph and
link together via the node parameters. Finally, we validate and execute the
graph. Once the graph has been built and validated, we can execute it many
times with different input data.
In this chapter, we illustrate this with a simple background subtrac-
tion example. Suppose we want to identify all the “foreground” pixels in
a scene. We have an image of the “background” (we will talk about how
to create such a background image in a later chapter) and a current im-
age of the same scene that might have some foreground objects in it. For
our purposes, foreground just means “not background.” One way to get
an idea of where the foreground objects are located is to simply subtract
the background image from the current image, take the absolute value of
all the pixel differences, and apply some threshold to these differences. In
other words, if a pixel in the current image is sufficiently different from
the background, we call it a foreground pixel. Now there are a number of
problems with this simple approach, for example, a small camera movement
or lighting change can make everything become “foreground,” but we will
ignore that for the purposes of this simple example.
Let us see how this looks in OpenVX. We need image data objects for
the background image, current image, difference image, and final (binary)
foreground image. The code at the top of the next page creates these.
OpenVX Programming Guide
https://doi.org/10.1016/B978-0-12-816425-9.00009-7
Copyright © 2020 Elsevier Inc.
All rights reserved. 35
36 OpenVX Programming Guide
vx_image bg_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8);
vx_image curr_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8);
vx_image diff_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8);
vx_image fg_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8);
In a real production code, we should check that all the data objects were
created successfully, but we leave this out of this sample code for simplicity.
In this example, we also need to create a threshold object and set it to some
initial value:
vx_threshold threshold =
vxCreateThresholdForImage(ctxt, VX_THRESHOLD_TYPE_BINARY,
VX_DF_IMAGE_U8, VX_DF_IMAGE_U8);
vx_status status;
vx_uint8 threshval = 10;
status = vxCopyThresholdValue(threshold, threshval,
VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);
In this example, we use 10 as the threshold, so if the grayscale value of
the current image is different from the background by more than 10, then
we call it a foreground pixel. We use the default values for the threshold
outputs, so the foreground pixels will be set to 255 (0xFF), and the rest will
be set to zero. Now that we have the necessary data objects, we can build a
simple graph and link it up.
3.1 Linking nodes
In OpenVX, nodes are linked via the parameters to the node creation func-
tions. If an output parameter of one node is the same OpenVX data object
as the input parameter of another node, then the nodes are linked via this
data object. There is no separate explicit “link” call. This enables OpenVX
code to look very similar to traditional non-graph code. For our back-
ground subtraction example, we just have two nodes, an absolute difference
(or absdiff) node and a threshold node (thresh). Here is the code to create
the graph and insert the nodes:
vx_graph g = vxCreateGraph(ctxt);
vx_node absdiff = vxAbsDiffNode(g, bg_image, curr_image, diff_image);
vx_node thresh = vxThresholdNode(g, diff_image, threshold, fg_image);
The first line creates the graph object g within our OpenVX context.
The next two lines create the absdiff and threshold nodes, respectively.
Other documents randomly have
different content
were dwindling in numbers, and it was this marriage of a Comte de Foix
with the heiress of Béarn which caused practically the extinction of one.
The modern department of the Ariège, of which the ancient Comté de
Foix formed the chief part, possesses few historical monuments dating
before the Middle Ages. There are numerous residential châteaux scattered
about, and the most splendid of them all is at Foix itself. Fine old churches
and monasteries, and quaint old houses are numerous; yet it is a region less
exploited by tourists than any other in France.
Not all these historic shrines remain to-day unspoiled and untouched.
Many of them were destroyed in the Revolution, but their sites and their
ruins remain. The mountain slopes of this region are thickly strewn with
watch-towers and observatories; and though all but fallen to the ground they
form a series of connecting historical links which only have to be
recognized to be read. The towers or châteaux of Quié, Tarascon-sur-
Ariège, Gudanne, Lourdat and Vic-Dessos are almost unknown to most
travellers. They deserve to become better known, however, especially
Lourdat, one of the most spectacularly endowed château ruins extant.
The fourteenth century was the most brilliant in the history of Foix.
These were the days of Gaston Phœbus; and the description of his reception
of Charles VI of France at Mazères, as given by the chroniclers, indicates
an incomparable splendour and magnificence. Gaston Phœbus, like Henri
de Béarn, was what might be called a good liver. Here is how he spent his
day—when he was not warring or building castles. He rose at noon and
after a mass he dined. Usually there were a great number of dishes; and, on
really great occasions, as on a fête or festin, the incredible number of two
hundred and fifty. These princes of the Pyrenees loved good cheer, and their
usage was to surcharge the tables and themselves with the good things until
the results were uncomfortable. Gaston’s two sons, Yvain and Gratain,
usually stood behind him at table, and the youngest son, another Gaston,
first tried all the dishes before his august father ate of them. He was weak
and sickly, a “mild and melancholy figure,” and no wonder! The feasting
terminated, Gaston and his court would pass into the Salle de Parlement,
“where many things were debated,” as the chroniclers put it. Soon entered
the minstrels and troubadours, while in the courts there were trials of skill
between the nobles of one house and another, stone throwing, throwing the
spear, and the jeu de paume. The count—“toujours magnifique” (no
chronicler of the time neglects to mention that fact)—distributed rewards to
the victors. After this there was more eating, or at least more drinking.
When he was not sleeping or eating or amusing himself, or conducting
such affairs as he could not well depute to another, such as the planning and
building of castles, Gaston occupied himself, like many other princes of his
time, with belles-lettres and poesy. He had four secrétaires to do his
writing; and it is possible that they may have written much which is
attributed to him, if the art of employing literary “ghosts” was known in
that day. He composed chansons, ballades, rondeaux and virelais, and
insisted on reading them aloud himself, forbidding any one to make a
comment on them. How many another author would like to have the same
prerogative!
Gaston Phœbus de Foix, so named because of his classic beauty, was
undoubtedly a great author in his day. This bold warrior wrote a book on the
manners and usage of hunting in mediæval times, entitled the “Miroir de
Phœbus;” and, while it might not pass muster among the masterpieces of
later French literature, it was a notable work for its time and literally a
mirror of contemporary men and manners in the hunting field.
Gaston de Foix was another gallant noble. He died at the age of twenty-
four at the Battle of Ravenna in 1512. Jacques Fournier, who became Pope
Benoit XII, also came from Foix.
The honour of being the most celebrated of the Counts of Foix may well
be divided by Gaston Phœbus (1343-1390) and Henri Quatre (1553-1610).
The latter was the last of the famous counts of the province; and he it was
who united it with the royal domain of France, thus sinking its identity for
ever, though his predecessors had done their utmost to keep its
independence alive.
During the Hundred Years War the Comtes de Foix, masters of the entire
middle chain of the Pyrenees, were the strongest power in the southwest;
and above all were they powerful because of their alliances and relations
with the Spanish princes, whose friendship and aid were greatly to be
desired, for their support meant success for their allies. This is proven,
absolutely, from the fact that, when the English were ultimately driven from
France, it was through the aid and support of Gaston Phœbus himself and
his successors, Archambaud, Jean I and Gaston IV.
The fifteenth century saw the apogee of the house of Foix. One of its
princes married Madeleine de France, sister of Louis XI. The sixteenth
century saw sad times during a long civil war of more than thirty years
duration. War among the members of a household or among one’s own
people is really an inexcusable thing. In the Comté the Abbey of Boulbonne
was destroyed. At Pamiers all the religious edifices were razed; and the
Abbey of St. Volusien at Foix, the special pride of the counts for ages, was
destroyed by fire.
Calm came for a period under the reign of Henri IV, at Paris; but, after
his death, local troubles and dissensions broke out again, inspired and
instigated by the wily Duc de Rohan, which culminated at Pamiers, where
the great Condé and Montmorenci appeared at the head of their troops.
The peace of Alais ended this final struggle; and, to assure the security
of the country, Richelieu gave the order to dismantle all the walls and
ramparts of the fortified places in the Comté, and all the châteaux-forts as
well. This was done forthwith, and that is why many a mediæval château in
these parts is in ruins to-day. The Château de Foix, by reason of its dignity,
was allowed to keep its towers and battlemented walls.
For a hundred and fifty years, that is up to the Revolution, Foix was
comparatively tranquil. Under the reign of Louis XIV, however, the region
saw the frequent passage of troops and warlike stores as they came and
went to the Spanish wars. This nearly ruined many dwellers in town and
country by reason of the tax they had to pay in money and provisions.
Like the Basques and the Béarnais the inhabitants of the Ariège, the
descendants of the old adherents of the Comtes de Foix, bear many traces of
their former independence and liberty. Civilization and their easy,
comfortable manner of living have not made of them a very robust race, but
they are possessed of much fairness of face and figure and gentleness of
manner.
The smugglers of feudal times, and considerably later times for that
matter, were the pest of the region. It was rude, hard work smuggling wines
or tobacco over the mountains, in and out of Spain, and its wages were
uncertain, but there were large numbers who embarked on it in preference
to grazing flocks and herds or engaging in other agricultural pursuits.
It was hard work for the smugglers of Foix to get their burdens up the
mountains, but they had a custom of rolling their load up into great balls
bound around with wool and thongs and rolling them down the other side.
Thus the labour was halved. The Romany chiel or gypsy adopted the
contraband business readily; and with the competition of the French and
Spanish, there were lively times on the frontier between Foix and Gascogne
and Spain and Andorra.
M. Thiers recounts an adventure in an auberge of the Pyrenees with such
a crew of bandits, and thought himself lucky to escape with his life.
The chief of the band, as the travellers were all sitting around the great
log fire, began cleaning his pipe with a long poignard-like knife which, he
volunteered, was ready to do other service than whittling bread or tobacco if
need be. The night passed off safely enough by reason of the arrival of a
squad of gendarmes, but the next night a whole house full of travellers were
murdered on the same spot.
The roads of the old Comté de Foix, a very important thing for many
who travel by automobile, are throughout excellent and extensive. There are
fourteen Routes Nationales and Départementales crossing in every
direction. The highway from Toulouse to Madrid runs via St. Girons and
Bayonne into Andorra by way of the valley of the Ariège, and to Barcelona
via Perpignan and the Col de Perthus.
The valley of the Ariège, to a large extent included in the Comté de Foix,
has a better preserved historical record than its neighbours on the east and
west.
In the ninth century the ruling comte was allied with the houses of
Barcelona and Carcassonne. His residence was at Foix from this time up to
the Revolution; and his rule embraced the valley of the Hers, of which
Mirepoix was the principal place, the mountain region taken from
Catalogne, and a part of the lowlands which had been under the scrutiny of
the Comtes de Toulouse.
CHAPTER XI
FOIX AND ITS CHÂTEAU
FOIX, of all the Préfectures of France of to-day, is the least
cosmopolitan. Privas, Mende and Digne are poor, dead, dignified relics of
the past; but Foix is the dullest of all, although it is a very gem of a smiling,
diffident little wisp of a city, green and flowery and astonishingly
picturesque. It has character, whatever it may lack in progressiveness, and
the brilliant colouring is a part of all the cities of the South.
Above the swift flowing Ariège in their superb setting of mountain and
forest are the towers and parapets of the old château, in itself enough to
make the name and fame of any city.
Architecturally the remains of the Château de Foix do not, perhaps, rank
very high, though they are undeniably imposing; and it will take a review of
Froissart, and the other old chroniclers of the life and times of the
magnificent Gaston Phœbus, to revive it in all its glory. A great state
residence something more than a mere feudal château, it does not at all
partake of the aspect of a château-fort. It was this last fact that caused the
Comtes de Foix, when, by marriage, they had also become seigneurs of
Béarn, to abandon it for Mazères, or their establishments at Pau or Orthez.
Foix nevertheless remained a proud capital, first independent, then as
part of the province of Navarre, then as a province of the Royaume de
France; and, finally, as the Préfecture of the Département of Ariège. The
population in later times has grown steadily, but never has the city
approached the bishopric of Pamiers, just to the northward, in importance.
Many towns in this region have a decreasing population. The great cities
like Toulouse and Bordeaux draw upon the youth of the country for
domestic employment; and, lately, as chauffeurs and manicurists, and in
comparison to these inducements their native towns can offer very little.
If one is to believe the tradition of antiquity the “Rocher de Foix,” the
tiny rock plateau upon which the château sits, served as an outpost when the
Phoceans built the primitive château upon the same site. Says a Renaissance
historian: “On the peak of one of nature’s wonders, on a rock, steep and
inaccessible on all sides, was situated one of the most ancient fortresses of
our land.”
In Roman times the site still held its own as one of importance and
impregnability. A representation of the château as it then was is to be seen
on certain coins of the period. This establishes its existence as previous to
the coming of the Visigoths in the beginning of the sixth century. The first
written records of the Château de Foix date from the chronicles of 1002,
when Roger-le-Vieux, Comte de Carcassonne, left to his heir, Bernard-
Roger, “La Terre et le Château de Foix.”
The Château de Foix owes its reputation to its astonishingly theatrical
site as much as to the historic memories which it evokes, though it is with
good right that it claims a legendary renown among the feudal monuments
of the Pyrenees. All roads leading to Foix give a long vista of its towered
and crenelated château sitting proudly on its own little monticule of rock
beside the Ariège. Its history begins with that of the first Comtes de Foix,
the first charter making mention thereof being the last will and testament of
Roger-Bernard, the first count, who died in 1002.
During the wars against the Albigeois the château was attacked by
Simon de Montfort three times, in 1210, 1212, 1213, but always in vain.
Though the surrounding faubourgs were pillaged and burned the château
itself did not succumb. It did not even take fire, for its rocky base gave no
hold to the flames which burned so fiercely around it.
The most important event of the château’s history happened in 1272
when the Comte Roger-Bernard III rebelled against the authority of the
Seneschal-Royal of Toulouse. To punish so rebellious a vassal, Philippe-le-
Hardi came forthwith to Foix at the head of an army, and himself undertook
the siege of the château. At the end of three days the count succumbed, with
the saying on his lips that it was useless to cut great stones and build them
up into fortresses only to have them razed by the first besiegers that came
along. Whatever the qualifications of the third Roger-Bernard were,
consistent perseverance was not one of them.
Just previous to 1215, after a series of intrigues with the church
authorities, the château became a dependence of the Pope of Rome; but at a
council of the Lateran the Comte Raymond-Roger demanded the justice
that was his, and the new Pope Honorius III made over the edifice to its
rightful proprietor.
During the wars of religion the château was the storm-centre of great
military operations, of which the town itself became the unwilling victim.
In 1561 the Huguenots became masters of the city.
Under Louis XIII it was proposed to raze the château, as was being done
with others in the Midi, but the intervening appeal of the governor saved its
romantic walls to posterity. In the reign of Louis XIV the towers of the
château were used as archives, a prison and a military barracks, and since
the Revolution—for a part of the time at least—it has served as a house of
detention. When the tragic events of the Reformation set all the Midi
ablaze, and Richelieu and his followers demolished most of the châteaux
and fortresses of the region, Foix was exempted by special orders of the
Cardinal-Minister himself.
Another war cloud sprang up on the horizon in 1814, by reason of the
fear of a Spanish invasion; and it was not a bogey either, for in 1811 and
1812 the Spaniards had already penetrated, by a quickly planned raid, into
the high valley of the Ariège.
In 1825 civil administration robbed this fine old example of mediæval
architecture of many of those features usually exploited by antiquarians. To
increase its capacity for sheltering criminal prisoners, barracks and
additions—mere shacks many of them—were built; and the original
outlines were lost in a maze of meaningless roof-tops. Finally, a quarter of a
century later, the rubbish was cleared away; and, before the end of the
century, restoration of the true and faithful kind had made of this noble
mediæval monument a vivid reminder of its past feudal glory quite in
keeping with its history.
Ground Plan of the Château de Foix
The actual age of the monument covers many epochs. The two square
towers and the main edifice, as seen to-day, are anterior to the thirteenth
century, as is proved by the design in the seals of the Comtes de Foix of
1215 and 1241 now in the Bibliothèque Nationale in Paris. In the fourteenth
century these towers were strengthened and enlarged with the idea of
making them more effective for defence and habitation.
CHÂTEAU DE FOIX
The escutcheons of Foix, Béarn and Comminges, to be seen in the great
central tower, indicate that it, too, goes back at least to the end of the
fourteenth century, when Eleanore de Comminges, the mother of Gaston
Phœbus, ruled the Comté.
Key of the Vaulting, Château de Foix.
Showing the Arms of the Comtes de
Foix
The donjon or Tour Ronde arises on the west to a height of forty-two
metres; and will be remarked by all familiar with these sermons in stone
scattered all over France as one of the most graceful. Legend attributes it to
Gaston Phœbus; but all authorities do not agree as to this. The window and
door openings, the mouldings, the accolade over the entrance doorway and
the machicoulis all denote that they belong to the latter half of the fifteenth
century. These, however, may be later interpolations.
Originally one entered the château from exactly the opposite side from
that used to-day. The slope leading up to the rock and swinging around in
front of the town is an addition of recent years. Formerly the plateau was
gained by a rugged path which finally entered the precincts of the fortress
through a rectangular barbican.
Finally, to sum it up, the pleasant, smiling, trim little city of Foix, and its
château rising romantically above it, form a delightful prospect. Well
preserved, well protected, and for ever free from further desecration, the
Château de Foix is as nobly impressive and glorious a monument of the
Middle Ages as may be found in France, as well as chief record of the
gallant days of the Comtes de Foix.
Foix’ Palais de Justice, built back to back with the rock foundations of
the château, is itself a singular piece of architecture containing a small
collection of local antiquities. This old Maison des Gouverneurs, now the
Palais de Justice, is a banal, unlovely thing, regardless of its high-sounding
titles.
In the Bibliothèque, in the Hôtel de Ville, there are eight manuscripts in
folio, dating from the fifteenth century, and coming from the Cathedral of
Mirepoix. They are exquisitely illuminated with miniatures and initials after
the manner of the best work of the time.
It was that great hunter and warrior, Gaston Phœbus who gave the
Château de Foix its greatest lustre.
It was here that this most brilliant and most celebrated of the counts
passed his youth; and it was from here that he set out on his famous
expedition to aid his brother knights of the Teutonic Order in Prussia. At
Gaston’s orders the Comte d’Armagnac was imprisoned here, to be released
after the payment of a heavy ransom. As to the motive for this particular act
authorities differ as to whether it was the fortunes of war or mere
brigandage.
They lived high, the nobles of the old days, and Froissart recounts a
banquet at which he had assisted at Foix, in the sixteenth century, as
follows:—
“And this was what I saw in the Comté de Foix: The Count left his
chamber to sup at midnight, the way to the great salle being led by twelve
varlets, bearing twelve illumined torches. The great hall was crowded with
knights and equerries, and those who would supped, saying nothing
meanwhile. Mostly game seemed to be the favourite viand, and the legs and
wings only of fowl were eaten. Music and chants were the invariable
accompaniment, and the company remained at table until after two in the
morning. Little or nothing was drunk.”
Froissart’s description of the table is simple enough, but he develops into
melodrama when he describes how the count killed his own son on the
same night—a tragic ending indeed to a brilliant banquet. “‘Ha! traitor,’ the
Comte said in the patois, as he entered his sleeping son’s chamber; ‘why do
you not sup with us? He is surely a traitor who will not join at table.’ And
with a swift, but gentle drawing of his coutel (knife) across his successor’s
throat he calmly went back to supper.” Truly, there were high doings when
knights were bold and barons held their sway. They could combat
successfully everything but treachery; but the mere suspicion of that
prompted them to take time by the forelock and become traitors themselves.
Foix has a fête on the eighth and ninth of September each year, which is
the delight of all the people of the country round about. Its chief centre is
the Allées de Vilote, a great tree-shaded promenade at the base of the
château. It is brilliantly lively in the daytime, and fairy-like at night, with its
trees all hung with great globes of light.
A grand ball is the chief event, and the “Quadrille Officiel” is opened
with the maire and the préfet at the head. After this comes la fête générale,
when the happy southrons know no limit to their gaieties. There are three
great shaded promenades, and in each is a ball with its attendant music. It is
a pandemonium; and one has to be habituated to distinguish the notes of
one blaring band from the others. The central park is reserved for the
country folk, that on the left for the town folk, and that on the right for the
nobility. This, at any rate, was the disposition in times past, and some sort
of distinction is still made.
In suburban Foix, out on the road to Pamiers, is the little village of St.
Jean-de-Vergues. It has a history, of course, but not much else. It is a mere
spot on the map, a mere cluster of houses on the Grande Route and nothing
more. In the days of the Comte Roger-Bernard, however, when he would
treat with the king of France, and showed his willingness to become a
vassal, its inhabitants held out beyond all others for an “indépendance
comtale.” They didn’t get it, to be sure, but with the arrival of Henri Quatre
on the throne of France, the vassalage became more friendly than enforced.
CHAPTER XII
THE VALLEY OF THE ARIÈGE
THE entire valley of the Ariège, from the Val d’Andorre until it empties
into the Garonne at Toulouse, contains as many historic and romantic
reminders as that of any river of the same length in France.
Saverdun and Mazères, between Toulouse and Pamiers, and perhaps fifty
kilometres north of Foix, must be omitted from no historical trip in these
parts. Saverdun sits close beside one of the few remaining columns which
formerly marked the boundary between Languedoc and Gascogne, a
veritable historical guide-post. It was one of the former fortified towns of
the Comté de Foix. It is an unimportant and unattractive enough place to-
day, if a little country town of France can ever be called unattractive, but it
is the head centre of innumerable châteaux and country houses of other
days hidden away on the banks of the Ariège. Mostly they are without a
traceable history, but everything points to the fact that they played an
important part in the golden days of chivalry, and such names as l’Avocat-
Vieux, Frayras, Larlenque, Madron, Pauliac and Le Vigne—the oldtime
manor of the family of Mauvasin—will suggest much to any who know
well their mediæval history.
A diligence runs to-day from Saverdun to Mazères, the birthplace of the
gorgeous and gallant Gaston of Foix, the hero of Ravenna. Mazères is a
most ancient little town, built on the banks of a small river, the Hers, and in
the thirteenth century was surrounded by important fortifications, now
mostly gone to build up modern garden walls. Around the old ramparts has
been laid out a series of encircling boulevards, which, as an expression of
civic improvement, is far and away ahead of the squares and circles of new
western towns in America. The encircling boulevard is one, if not the chief,
charm of very many French towns.
The ruins of the ancient château where was born the celebrated Gaston
are still seen, but nothing habitable is left to suggest the luxury amid which
the youth was brought up. Near by are the châteaux of Nogarède and
Nassaure, each of them reminiscent of family names writ large in the
history of Foix.
Another dozen kilometres southward towards Foix is Pamiers. It is
extremely probable that provincial France has changed its manners
considerably since the Revolution, but one can hardly believe of Pamiers,
to-day a delightful little valley town, all green and red and brown, that a
traveller with a jaundiced eye once called it “an ugly, stinking, ill-built hole
with an inn—of sorts,” This is not the aspect of the city, nor does it describe
the Hôtel Catala.
Pamiers owes its origin to the erection of a feudal château by Comte
Roger II on his return from the Holy Land, and which he called Apamea or
Apamia, in memory of his visit to Apamée in Syria. Evolution has readily
transformed the name into Pamiers. Virtually, so far as its lands went, the
place belonged to a neighbouring abbey, but as the monks were forced to
call upon the Comtes de Foix to aid them in protecting their property from
the Comtes de Carcassonne, the title rights soon passed to the ruling house
of Foix. In 1628 Condé pillaged and sacked the city, and not a vestige now
remains of its once proud château, save such portions as may have been
built into and hidden in other structures. The site of the old château is
preserved in the memory only by the name of Castellat, which has been
given to a singularly beautiful little park and promenade.
It was in the thirteenth century that a Bishop of Pamiers, the legate of
Pope Boniface VIII, insulted Philippe-le-Bel in full audience of his
parlément. The king, resentful, drove him from the council, and a Bull of
Pope Boniface delivered the bishop to an ecclesiastical tribunal. So far, so
good, but Boniface issued another Bull demanding that the king of France
submit to papal power in matters temporal as well as in matters spiritual.
Thus a pretty quarrel ensued, beginning with the famous letter from the
king, which opened thus: “Philippe, by the grace of God, King of the
French, to Boniface, the pretended Pope, has little or no reason for
homage....”
Pamiers itself is a dull little provincial cathedral town, lying low in a
circle of surrounding hills. Its churches are historically famous, and
architecturally varied and beautiful, and the octagonal belfry of its cathedral
(1512), in the style known as “Gothic-Toulousain,” is particularly
admirable.
Mirepoix, a dozen kilometres east of Pamiers, is interesting. The
Seigneurie of Mirepoix became an appanage of Guy de Levis, maréchal in
the army of Simon de Montfort in the thirteenth century, but the legislators
of Revolutionary times, disregarding the usage of five centuries, coupled
the control of the affairs of the region with those of Foix, from which it had
indeed been separated long ages before.
Mirepoix has, nevertheless, an individuality and a history quite its own.
In 1317 it was made a bishopric, and was under the immediate control of
the Seneschalship of Carcassonne. It had, by parent right, a certain
attachment for Foix, but by the popular consent of its people none at all;
thus it lay practically under the sheltering wing of Languedoc.
The descendants of Guy de Levis were distinguished in the army, in
diplomacy and held many public offices of trust at Paris. Under Louis XV
the last representative of the family was made a “Duc, Maréchal de France
et Gouverneur de Languedoc.” It was his cousin, François de Levis-Ajac
(from whom Levis opposite Quebec got its name), who became also
Maréchal de France, and illustrious by reason of his defence of Canada.
The Château de Montségur, in the valley of the Hers, was the scene of
the last stand of the Albigeois tracked to their death by the inquisitors.
Just westward of Foix is La Bastide-de-Serou, founded in 1254, another
of those ancient bastides with which this part of the Midi was covered in
mediæval times. To-day it is a mere nothing on the map, and not much more
in reality, a dull, sad town, whose only liveliness comes from the
exploitations of a company whose business it is to dig phosphate and
bauxite from the hillsides round about.
Below La Bastide is the Château de Bourdette, charmingly set about
with vines in a genuine pastoral fashion. For a neighbour, not far away,
there is also the Château de Rodes, set in the midst of a forest of mountain
ash and quite isolated. Either, if they are ever put on the market (for they are
inhabitable to-day), would make a good retiring spot for one who wanted to
escape the strenuous cares and hurly-burly of city life.
South of Foix is Tarascon-sur-Ariège, a name which has a familiar sound
to lovers of fiction and readers of Daudet. It was not at Tarascon-sur-Ariège
where lived Daudet’s estimable bachelor, Tartarin, but Tarascon-sur-Rhône
in Provence. Daudet pulled the latter smug little town from obscurity and
oblivion—even though the inhabitants said that he had slandered them—but
nothing has happened that gives distinction to the Tarascon of the Pyrenees
since the days when its seigneurs inhabited its château.
Tarascon-sur-Ariège
Reminders of the town’s mediæval importance are few indeed, and of its
château only a lone round tower remains. There are two fortified gateways
in the town still above ground, and two thirteenth-century church towers
which take rank as admirable mediæval monuments.
Tarascon was one of the four principal fortified towns of the Comté de
Foix, but suffered by fire, and for ever since has languished and dozed its
days away, so that not even a passing automobile will wake its dwellers
from their somnolence. Tarascon has a fine and picturesque bridge over the
Ariège which intrudes itself in the foreground from almost every view-
point. It is not old, however, but the work of the last century.
Here nearly everything is of the mouldy past and rusty with age and
tradition, though there is a local iron industry something considerable in
extent.
The highroad from Foix into Andorra cuts the town directly in halves,
and on either side are narrow, climbing streets running up the hillside from
the river bank, but architectural or topographical changes have been few
since the olden times. Tarascon’s population—though the place is the
market town of the commune—has, in a hundred years, fallen from fifteen
hundred to fourteen hundred and forty five, to give exact statistical figures,
which are supposed not to lie. Such observations in France really prove
nothing, not even that signs of progress are wanting, nor that folk are less
prosperous; they simply suggest that its cities and towns are self-satisfied
and content, and are not ambitious to outdistance their neighbours in
alleged civic improvements of doubtful taste—always at the tax-payers’
expense.
Tarascon of itself might well be omitted from a Pyrenean itinerary, but
when one includes the neighbouring church of Notre Dame de Sabart—a
place of pilgrimage for the faithful of the whole region of the Pyrenees on
the eighth and fifteenth of September—the case were different. It is one of
the sights and shrines of the region, as is that of Stes. Maries-de-la-Mer in
Provence, or Notre Dame de Laghat in the old Comté de Nice.
The old abbey-fortress built here by Charlemagne has disappeared, but
the great Romanesque church, with its three great naves, is avowedly built
up from the remains of the former edifice. Most of Charlemagne’s
handiwork has vanished throughout his kingdom, but the foundations
remain, here and there, and upon them has been built all that is best and
most enduring in Gaul.
In the environs it was planned to make a great centre of affairs, but
destiny and the Comtes de Foix ruled otherwise, though, curiously enough,
up to the Revolution the “Prétres de Sabart” ruled with an iron-bound
supremacy many of the affairs of neighbouring parishes which were no
business of theirs. It was church and state again in conflict, but the
Revolution finished that for the time being.
Like many of the pardons of Brittany, or the fête of Les Saintes Maries
in Provence, the fête of Notre Dame de Sabart commences as a religious
function, but degenerates finally into a Fête Profane, with dancing, bull-
baiting, and eating and drinking to the full. It is perhaps not a wholly
immoral aspect that the fête takes on; certainly the participants do not act in
any manner outrageous; but by contrast the thing is bound to be remarked
by westerners, and probably misjudged and set down as something worse
than it is. Bull-baiting, for instance, sounds bad, but when one learns that it
consists only of trying to snatch a ribbon rosette from between the bull’s
horns—for a prize of three francs for a blue one, and five francs for a red
one, the bull carrying the red rosette being, supposedly, more vicious and
savage than the others—the whole thing resolves itself into a simple,
harmless amusement, far more dangerous for the amateur rosette picker
than the bull, who really seems to enjoy it.
Vic Dessos, just southwest of Tarascon, is a quaint little mountain town,
with the ruins of the Château de Montréal and a twelfth-century church as
attractions for the traveller. The savage surroundings of Vic, the denuded
mountain peaks, and the deep valleys, bring tempests and thunderstorms in
their train with astonishing violence and frequency. The clouds roll down
like a pall, suddenly, at any time of the year, and as quickly pass away
again. The phenomena have been remarked by many travellers in times
past, and one need not fear missing it if he stays anything over three hours
within a fifty-kilometre radius. If this offers anything of a sensation to one,
Vic Dessos should be visited. You can arrive by diligence from Tarascon,
and can get comfortably in out of the rain at the excellent Hôtel Benazet.
From Tarascon to Ax-les-Thermes, still in the valley of the Ariège, is
twenty-five kilometres of superb roadway. All the way are strung out
groups of dainty villages surrounded with cultivated country. Here and there
is an isolated mass of rock, a round watch-tower, or a ruined fortress, still
possessing its crenelated walls to give an attitude of picturesqueness. There
are innumerable little villages, a whole battery of them, linked together. At
the end of this long peopled highway is an unpretentious mediæval country
house, of that class known as a gentilhommière, of fawn-coloured stone,
and still possessing its two flanking sentinel towers preserved in all the
romantic grimness of their youth.
At the junction of the Ariège with the Ascou, the Oriège, the Lauze and
the Foins is Ax-les-Thermes—the ancient Aquæ of the Romans, and now a
“thermal station” of the first rank. Primarily Ax is noted for its sulphurous
waters, but for the lover of romantic days and ways its architectural and
Historical monuments are of the first consideration. The ruins of the
Château des Maures, the ancient Castel Maü, are the chief of these
monuments, while a neighbouring peak of rock bears aloft an enormous
square tower surmounted by a statue of the Virgin.
There are sixty-one “sources” at Ax-les-Thermes giving a supply of
medicinal waters. In part they were known to the Romans, and in 1260
Saint Louis founded a hospital here for sick soldiers returning from the
Crusades.
Ax-les-Thermes is not a howlingly popular watering place, but it is far
more delightful than Luchon, Cauterets or Bigorre, if quaintness of
architecture, manners and customs, and modesty of hotel prices count for
anything.
The Porte et Pont d’Espagne at Ax is one of the most interesting
architectural reminders of the past that one will find throughout the
Pyrenees. The bridge itself is but a diminutive span carrying a narrow
roadway, which if not forbidden to automobile traffic should be, for the
negotiating of this bridge and road, and the low, arched gateway at the end,
will come very near to spelling disaster for any who undertakes it.
Throughout the neighbourhood one sees more than an occasional
yawning pit’s mouth. All through the Comté de Foix were exploited, and
are yet to some extent, iron mines and forges, the latter known as Forges
Catalans. Roger-Bernard, Comte de Foix, in 1293 gave the first charter to
the mine-promotors of the neighbourhood, and the industry flourished in
many parts of the Comté until within a few generations, when, apparently,
the supply of mineral was becoming exhausted.
At Luzenac, on the line between Tarascon and Ax, one turns off the road
and in a couple of hours, if he is a good brisk walker, makes the excursion
to the château-à-pic of Lourdat. There is a little village of the same name at
the base of the rocky peak which holds aloft the château, but that doesn’t
count.
Without question this Château de Lourdat ranks as one of the most
spectacular of all the Pyrenean châteaux. Its rank in history, too, is quite in
keeping with its extraordinary situation, though nothing very startling ever
happened within its walls. It dates from the thirteenth and fifteenth
centuries, and outside that of the capital of Foix was the most efficient
stronghold the counts possessed. Louis XIII demolished the edifice, in part,
fearing its powers of resistance, and as a base from which some new project
might be launched against him. Accordingly, it is a ruin to-day, but in spite
of this there are still left four pronounced lines of fortifications before one
comes to the inner precincts of the château. For this reason alone it ranks as
one of the most strongly defended of all contemporary feudal works. Even
the old Cité de Carcassonne has but two encircling walls.
The square donjon rising in the middle is in the best style of that
magnificent royal builder, Gaston Phœbus, and is reminiscent of the works
of Foulques Nerra in mid-France. There is also a great ogive-arched portal,
or gateway, which made still another defence to be scaled before one finally
entered within.
In situation and general spectacular effect the Château de Lourdat takes a
very near rank to that rock-perched château at Le Puy—“the most
picturesque spot in the world.”
Château de Lourdat
CHAPTER XIII
ST. LIZIER AND THE COUSERANS
LE Pays de Couserans lies in the valley of the Salat, in the mid-Pyrenees,
hemmed in by Foix, Comminges and Spain. Its name is derived from the
Euskarans, an Iberian tribe who were here on the spot in the dark ages.
The history of the Couserans is not known to anything like the extent of
its neighbouring states, and is, accordingly, very little travelled by strangers
from afar, save long-bearded antiquarians who come to study St. Lizier, and
regret that they were not obliged to come on donkey-back as of old, instead
of by rail or automobile. The trouble with antiquarianism, as a profession,
or a passion, is that it leads one to fall into a sleepy unprogressiveness
which comports little with the modern means at hand for doing things. A
photographic plate of a curious Roman inscription is far more truthful and
convincing than the most painstaking Ruskinese pencil drawing ever
limned, and a good “process-cut” of the broad strokes of some facile
modern artist’s brush is more typical of the characteristics of a landscape
than the finest wood or steel engraving our grandfathers ever knew.
If you like grand mountains, here in Couserans is Mont Vallier, a superb
giant of the central chain of the Pyrenees. If it is sweet sloping valleys that
you prefer, here they are in all their unspoiled wildness, for the railway
actually does stop at St. Girons. If an ice-cold mountain stream would
please your fancy, there is the Salat and its tributaries, flowing down by St.
Girons and St. Lizier into the Garonne. And, finally, if you wish to roll back
the curtain of time you will see in old St. Lizier a stage set with the
accessories the reminiscent splendours of which will be scarcely equalled
by any other feudal bourg of France.
There is no region in the Pyrenees of which less is known historically
than the Valley of the Salat. A vicomte reigned here in the sixteenth century,
but the seigneury was divided among different branches of the family soon
after; and, if they had an archivist among them, he failed to preserve his
documents along with the written history of the greater affairs of Toulouse
and Foix. Soon religious and civil troubles began to press and much of
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com

More Related Content

PDF
"The OpenVX Computer Vision and Neural Network Inference Library Standard for...
PDF
"The OpenVX Computer Vision and Neural Network Inference Library Standard for...
PDF
“The OpenVX Standard API: Computer Vision for the Masses,” a Presentation fro...
PDF
“The OpenVX Standard API: Computer Vision for the Masses,” a Presentation fro...
PDF
"The OpenVX Hardware Acceleration API for Embedded Vision Applications and Li...
PDF
"The OpenVX Hardware Acceleration API for Embedded Vision Applications and Li...
PDF
"New Standards for Embedded Vision and Neural Networks," a Presentation from ...
PDF
"New Standards for Embedded Vision and Neural Networks," a Presentation from ...
"The OpenVX Computer Vision and Neural Network Inference Library Standard for...
"The OpenVX Computer Vision and Neural Network Inference Library Standard for...
“The OpenVX Standard API: Computer Vision for the Masses,” a Presentation fro...
“The OpenVX Standard API: Computer Vision for the Masses,” a Presentation fro...
"The OpenVX Hardware Acceleration API for Embedded Vision Applications and Li...
"The OpenVX Hardware Acceleration API for Embedded Vision Applications and Li...
"New Standards for Embedded Vision and Neural Networks," a Presentation from ...
"New Standards for Embedded Vision and Neural Networks," a Presentation from ...

Similar to Openvx Programming Guide 1st Edition Frank Brill (20)

PDF
"Recent Developments in Khronos Standards for Embedded Vision," a Presentatio...
PDF
"Recent Developments in Khronos Standards for Embedded Vision," a Presentatio...
PDF
OpenVX 1.1 Reference Guide
PDF
OpenVX 1.1 Reference Guide
PDF
“OpenVX 1.3: An Open Standard for Computer Vision Software Acceleration,” a P...
PDF
“OpenVX 1.3: An Open Standard for Computer Vision Software Acceleration,” a P...
PDF
"Portable Performance via the OpenVX Computer Vision Library: Case Studies," ...
PDF
"Portable Performance via the OpenVX Computer Vision Library: Case Studies," ...
PDF
"The Vision Acceleration API Landscape: Options and Trade-offs," a Presentati...
PDF
"The Vision Acceleration API Landscape: Options and Trade-offs," a Presentati...
PDF
"Update on Khronos Standards for Vision and Machine Learning," a Presentation...
PDF
"Update on Khronos Standards for Vision and Machine Learning," a Presentation...
PDF
OpenVX 1.3 Reference Guide
PDF
OpenVX 1.3 Reference Guide
PDF
"Making OpenCV Code Run Fast," a Presentation from Intel
PDF
"Making OpenCV Code Run Fast," a Presentation from Intel
PDF
Introduction to OpenVX
PDF
Introduction to OpenVX
PDF
OpenVX 1.2 Reference Guide
PDF
OpenVX 1.2 Reference Guide
"Recent Developments in Khronos Standards for Embedded Vision," a Presentatio...
"Recent Developments in Khronos Standards for Embedded Vision," a Presentatio...
OpenVX 1.1 Reference Guide
OpenVX 1.1 Reference Guide
“OpenVX 1.3: An Open Standard for Computer Vision Software Acceleration,” a P...
“OpenVX 1.3: An Open Standard for Computer Vision Software Acceleration,” a P...
"Portable Performance via the OpenVX Computer Vision Library: Case Studies," ...
"Portable Performance via the OpenVX Computer Vision Library: Case Studies," ...
"The Vision Acceleration API Landscape: Options and Trade-offs," a Presentati...
"The Vision Acceleration API Landscape: Options and Trade-offs," a Presentati...
"Update on Khronos Standards for Vision and Machine Learning," a Presentation...
"Update on Khronos Standards for Vision and Machine Learning," a Presentation...
OpenVX 1.3 Reference Guide
OpenVX 1.3 Reference Guide
"Making OpenCV Code Run Fast," a Presentation from Intel
"Making OpenCV Code Run Fast," a Presentation from Intel
Introduction to OpenVX
Introduction to OpenVX
OpenVX 1.2 Reference Guide
OpenVX 1.2 Reference Guide
Ad

Recently uploaded (20)

PDF
Sports Quiz easy sports quiz sports quiz
PPTX
Final Presentation General Medicine 03-08-2024.pptx
PDF
Microbial disease of the cardiovascular and lymphatic systems
PDF
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
PDF
Basic Mud Logging Guide for educational purpose
PDF
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
PDF
Classroom Observation Tools for Teachers
PPTX
Introduction_to_Human_Anatomy_and_Physiology_for_B.Pharm.pptx
PDF
VCE English Exam - Section C Student Revision Booklet
PDF
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
PPTX
Pharmacology of Heart Failure /Pharmacotherapy of CHF
PDF
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
PDF
Insiders guide to clinical Medicine.pdf
PDF
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
PPTX
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
PDF
Pre independence Education in Inndia.pdf
PPTX
Cell Structure & Organelles in detailed.
PDF
Computing-Curriculum for Schools in Ghana
PDF
Supply Chain Operations Speaking Notes -ICLT Program
PDF
Anesthesia in Laparoscopic Surgery in India
Sports Quiz easy sports quiz sports quiz
Final Presentation General Medicine 03-08-2024.pptx
Microbial disease of the cardiovascular and lymphatic systems
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
Basic Mud Logging Guide for educational purpose
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
Classroom Observation Tools for Teachers
Introduction_to_Human_Anatomy_and_Physiology_for_B.Pharm.pptx
VCE English Exam - Section C Student Revision Booklet
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
Pharmacology of Heart Failure /Pharmacotherapy of CHF
grade 11-chemistry_fetena_net_5883.pdf teacher guide for all student
Insiders guide to clinical Medicine.pdf
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
1st Inaugural Professorial Lecture held on 19th February 2020 (Governance and...
Pre independence Education in Inndia.pdf
Cell Structure & Organelles in detailed.
Computing-Curriculum for Schools in Ghana
Supply Chain Operations Speaking Notes -ICLT Program
Anesthesia in Laparoscopic Surgery in India
Ad

Openvx Programming Guide 1st Edition Frank Brill

  • 1. Openvx Programming Guide 1st Edition Frank Brill download https://ebookbell.com/product/openvx-programming-guide-1st- edition-frank-brill-11109984 Explore and download more ebooks at ebookbell.com
  • 2. Here are some recommended products that we believe you will be interested in. You can click the link to download. Openx Ad Server Beginners Guide Murat Yilmaz https://ebookbell.com/product/openx-ad-server-beginners-guide-murat- yilmaz-2310212 Openvz Essentials Create And Administer Virtualized Containers On Your Server Using The Robust Openvz Mark Furman https://ebookbell.com/product/openvz-essentials-create-and-administer- virtualized-containers-on-your-server-using-the-robust-openvz-mark- furman-5472116
  • 7. Academic Press is an imprint of Elsevier 125 London Wall, London EC2Y 5AS, United Kingdom 525 B Street, Suite 1650, San Diego, CA 92101, United States 50 Hampshire Street, 5th Floor, Cambridge, MA 02139, United States The Boulevard, Langford Lane, Kidlington, Oxford OX5 1GB, United Kingdom Copyright © 2020 Elsevier Inc. All rights reserved. No part of this publication may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or any information storage and retrieval system, without permission in writing from the publisher. Details on how to seek permission, further information about the Publisher’s permissions policies and our arrangements with organizations such as the Copyright Clearance Center and the Copyright Licensing Agency, can be found at our website: www.elsevier.com/permissions. This book and the individual contributions contained in it are protected under copyright by the Publisher (other than as may be noted herein). Notices Knowledge and best practice in this field are constantly changing. As new research and experience broaden our understanding, changes in research methods, professional practices, or medical treatment may become necessary. Practitioners and researchers must always rely on their own experience and knowledge in evaluating and using any information, methods, compounds, or experiments described herein. In using such information or methods they should be mindful of their own safety and the safety of others, including parties for whom they have a professional responsibility. To the fullest extent of the law, neither the Publisher nor the authors, contributors, or editors, assume any liability for any injury and/or damage to persons or property as a matter of products liability, negligence or otherwise, or from any use or operation of any methods, products, instructions, or ideas contained in the material herein. Library of Congress Cataloging-in-Publication Data A catalog record for this book is available from the Library of Congress British Library Cataloguing-in-Publication Data A catalogue record for this book is available from the British Library ISBN: 978-0-12-816425-9 For information on all Academic Press publications visit our website at https://www.elsevier.com/books-and-journals Publisher: Mara Conner Acquisitions Editor: Tim Pitts Editorial Project Manager: Joshua Mearns Production Project Manager: Kamesh Ramajogi Designer: Miles Hitchen Typeset by VTeX
  • 8. Contents About the authors xi Foreword xiii Acknowledgments xv 1. Introduction 1 1.1. What is OpenVX and why do we need it? 2 1.2. Portability 4 1.3. OpenVX data objects 5 1.3.1. Opaque memory model 5 1.3.2. Object attributes 6 1.4. Graph API 6 1.5. Virtual objects 8 1.6. Deep neural networks 9 1.7. Immediate mode API 9 1.8. OpenVX vs. OpenCV and OpenCL 10 1.9. OpenVX versions 12 1.10. Prerequisites 12 1.11. Code samples 12 2. Build your first OpenVX program 15 2.1. First things first 15 2.1.1. How to get the OpenVX sample implementation 16 2.1.2. Building the examples 16 2.2. Immediate mode 17 2.2.1. Our first very simple example in immediate mode 17 2.3. Graph mode 22 2.3.1. Creating the graph 22 2.3.2. Connecting to parameters and running the graph 24 2.3.3. Running example 4 25 2.3.4. Code for example 4 25 2.4. Image input and output 30 2.4.1. Converting to and from portable pixmap format 30 2.4.2. Other libraries for input and output of images 31 2.4.3. Image processing exercises 31 2.4.4. Answers, or at least hints, for the exercises 32 3. Building an OpenVX graph 35 3.1. Linking nodes 36 3.2. Virtual images 37 v
  • 9. vi Contents 3.3. Graph verification and execution 39 3.4. Parameter validation 43 3.5. What can be changed at runtime? 44 3.6. Example 46 4. Using the graph API to write efficient portable code 47 4.1. OpenVX graph 48 4.1.1. OpenVX graph is a bipartite DAG 48 4.1.2. Intermediate results as virtual data objects 49 4.1.3. A graph example 49 4.2. Node parameters 52 4.3. Graph parameters 53 4.4. Execution model 55 4.4.1. Synchronous execution 55 4.4.2. Asynchronous execution 57 4.5. Control flow 58 4.6. User kernels and nodes 58 4.7. The OpenVX pipelining extension 60 5. Deploying an OpenVX graph to a target platform 65 5.1. Graph factories 66 5.1.1. An example of a graph factory 66 5.1.2. Using a graph factory 69 5.1.3. When to use a graph factory for deployment 71 5.1.4. Graph factory pros and cons 71 5.2. The XML extension 72 5.2.1. Pros and cons of the XML extension 74 5.2.2. When to use the XML extension for deployment 75 5.3. The export and import extension 75 5.3.1. Exporting a graph with the export and import extension 75 5.3.2. Importing a graph with export and import extension 78 5.3.3. Pros and cons of the export and import extension 81 5.3.4. When to use the export and import extension 82 6. Basic image transformations 85 6.1. OpenVX image object 85 6.2. Image filtering 87 6.2.1. Simple image filtering example 87 6.2.2. Custom convolution 89 6.3. Regions of interest 91 6.3.1. Reading from regions of interest 91 6.3.2. Writing to regions of interest 92 6.4. Feature extraction 95
  • 10. Contents vii 6.4.1. Hough transform 95 6.4.2. Postprocessing hough transform results 100 6.5. Geometric image transformations 108 6.5.1. Undistortion implemented with remap 108 6.5.2. Perspective transformations 114 7. Background subtraction and object detection 125 7.1. A threshold-based object detector 125 7.2. Adding alpha blending to the background model 129 7.3. Applying morphology operators to the foreground 133 8. Computational photography 135 8.1. Naïve image blending 135 8.1.1. The algorithm 136 8.1.2. Preparing stitching data 137 8.1.3. The stitching algorithm implementation 139 8.2. Obtaining intermediate results 143 8.3. Multiband image blending 145 8.3.1. The main graph 148 8.4. Building a Gaussian pyramid for the blending weights 153 8.5. Implementing Laplacian pyramid functions with OpenVX graph 157 8.5.1. Implementing a Laplacian pyramid 157 8.5.2. Implementing Laplacian pyramid reconstruction 160 9. Tracking 163 9.1. Introduction 163 9.2. Tracking methods 164 9.3. Optical flow 165 9.4. The aperture problem 166 9.5. The Lucas–Kanade algorithm for sparse feature sets 166 9.6. Bouguet’s implementation using image pyramids 168 9.6.1. The OpenVX API for optical flow 169 9.7. Selection of features to track 171 9.7.1. The OpenVX API for FAST corners 172 9.8. Centroid tracking 172 9.8.1. Difficulties 174 9.8.2. User nodes to handle centroid tracking 175 9.9. Example of tracking using OpenVX 177 9.9.1. Calculating the initial tracking data 180 9.9.2. Calculating the bounding box position in the next frame 185 9.9.3. Running the tracking example application 194 9.10. Multiple objects 195 9.11. Occlusion and trajectories 195
  • 11. viii Contents 9.12. The mirror ball and other failings of our algorithm 197 9.13. Image stabilization, panning, and zooming 198 10. Neural networks 205 10.1. OpenVX neural network extension 206 10.2. Inserting an entire neural network into an OpenVX graph as a single node 207 10.3. NNEF: the Khronos neural network exchange format 208 10.4. Import NNEF models into OpenVX 209 10.4.1. Inserting a neural network into an OpenVX graph 210 10.4.2. Summary 214 11. OpenVX safety-critical applications 215 11.1. Safety-critical applications and standards 216 11.1.1. What sort of applications have safety implications? 216 11.1.2. Applicable standards 218 11.2. What is in these standards? 222 11.3. What could possibly go wrong? 223 11.3.1. A spelling mistake with potentially disastrous consequences 223 11.3.2. Misuse of enumerated types 223 11.4. Errors and avoiding them 223 11.4.1. Requirement, design, programming and transient errors 223 11.4.2. Defensive programming 225 11.4.3. Feature subsets 226 11.5. How to do agile development and still meet the ISO26262 requirements 226 11.6. Determinism 227 11.7. OpenVX-SC 228 11.7.1. Differences between OpenVX and OpenVX-SC 229 11.7.2. Drawbacks of OpenVX-SC 230 11.7.3. A targeted shim layer 233 11.8. Programming with C++ 233 11.9. Data objects 234 11.9.1. Constructors and destructors 234 11.9.2. Query attribute and SetAttribute 234 11.9.3. Arrays of references 235 11.10. A practical example 235 11.10.1. vx_reference 236 11.10.2. VxReference with paranoia 240 11.10.3. queryReference and the tagmap template 240 11.10.4. Arrays of references 242 11.10.5. vx_context – the VxContext object 244 11.10.6. vx_import 249 11.10.7. vx_graph 249
  • 12. Contents ix 11.10.8. Mapping other objects 256 11.10.9. The delay and object array classes 256 11.11. Learning and modification of software in safety-critical systems 258 12. Efficient data input/output 265 12.1. Introduction to efficient data I/O in OpenVX 265 12.2. Efficient data I/O from and to external sources 266 12.3. Gaining access to data objects via map and unmap 273 12.3.1. Mapping image patches 274 12.3.2. More on pixel addressing 277 12.3.3. Mapping arrays 280 12.3.4. Mapping distributions 281 12.3.5. Mapping LUTs 282 12.3.6. Mapping remap objects 282 12.3.7. Mapping tensors 284 12.4. Conclusion 285 13. Interoperability between OpenVX and other frameworks 287 13.1. OpenCV 288 13.1.1. Convert an OpenCV image to OpenVX 289 13.1.2. Convert an OpenVX image to OpenCV 292 13.1.3. Converting other data types 294 13.2. OpenCL 296 13.2.1. OpenCL in a nutshell 296 13.2.2. Interoperability with OpenCL 302 13.2.3. Summary 308 14. Existing implementations and platforms for OpenVX 311 14.1. CPU implementations 312 14.2. DSP implementations 314 14.3. GPU implementations 316 14.4. Special-purpose hardware implementations 318 14.5. FPGA implementations 318 14.6. Hybrid System-on-Chips (SoCs) 319 15. Changes in OpenVX 1.3 321 15.1. Feature sets 321 15.1.1. Binary images 323 15.2. Safety-critical application support 325 15.3. Removal of bidirectional parameters 327 15.4. User structures 328 15.5. Miscellaneous changes 328
  • 13. x Contents A. The list of OpenVX data objects and their attributes 331 B. The list of computer vision graph API nodes 337 B.1. Image arithmetics and control flow 337 B.2. Image format conversions 338 B.3. Image filtering and statistics 339 B.4. Geometric transformations 339 B.5. Feature extraction 340 B.6. Tensor functions 341 References 343 Index 347
  • 14. About the authors Frank Brill manages Vision and AI software development for Cadence’s Tensilica Imaging and Vision DSP organization. He began his professional career developing network management software at Bell Communications Research. He then returned to school to earn a Ph.D., which he took to Texas Instruments to do computer vision research and development for video surveillance applications. He then moved into silicon device pro- gram management, where he was responsible for several systems-on-chip for digital still cameras and other multimedia devices. Since then, Frank has managed computer vision R&D groups at TI, NVIDIA, Samsung, and now at Cadence, and has represented all four companies in the Khronos OpenVX working group. He joined Cadence in 2016 and served as the chairperson of the OpenVX working group from 2016 to 2019. Frank holds Bachelor’s degrees in Mathematics and Computer Science from the University of Alaska, Fairbanks, and Master’s and Ph.D degrees in Com- puter Science from the University of Virginia. Victor Erukhimov is currently the CEO of Itseez3D, the company that democratizes 3D model creation. He also cofounded Itseez, Inc., which focused on developing computer vision solutions running on embedded platforms, specifically automotive safety systems. He successively held the positions of CTO, CEO, and President at Itseez when the company was ac- quired by Intel Corporation in 2016. Victor was the chair of the OpenVX working group in 2012–2016, helping to create the standard for cross- platform computer vision API. Before moving to the field of computer vision, he studied plasma physics at the Institute of Applied Physics, Russian Academy of Sciences, and tried to solve (without much success) the prob- lem of electron cyclotron heating. Victor was a member of the OpenCV development team. He participates in the development and maintenance of the OpenCV library and serves as the director of the OpenCV Foundation board. He is the author of about 30 papers and several US and international patents. Radhakrishna Giduthuri is currently a Principal Engineer at Intel, fo- cusing on software architecture for Intel AI Accelerators. Prior to working at Intel, he built computer vision, deep learning, and video compres- sion software acceleration libraries for AMD GPUs & CPUs. He has an xi
  • 15. xii About the authors extensive background with software architecture, development, and per- formance tuning for various computer architectures ranging from general purpose DSPs, customizable DSPs, media processors, heterogeneous pro- cessors, GPUs, and several CPUs. He is the current editor of the Khronos OpenVX working group and a member of Khronos NNEF (Neural Net- work Exchange Format) and OpenCL Safety-Critical working groups. For several years, he was a member of the SMPTE Video Compression Stan- dardizing Committee. He was the Outstanding Leadership and Professional Services Award Recipient for the IEEE Central Area in 2016. Radhakr- ishna earned a Master’s degree from IIT Kharagpur, India. Stephen Ramm is currently a principal software engineer with ETAS, a subsidiary of Bosch, where he is working on reliable frameworks and development environments for advanced functionality in the automotive, rail and other safety-critical industries. Until late 2017, he was Director of AI and Vision software at Imagination Technologies, where one of his responsibilities was the team producing an implementation of OpenVX ac- celerated by proprietary GPU architecture. Prior to that, Stephen has held various positions in technical management, research, design, and applied mathematics with experience in fields as varied as optical design, nutri- tional analysis, advanced statistics and calibration techniques, RF electron- ics, NMR, seismic analysis and detonators, electronics at board and silicon level, and development tools for the games industry. Stephen was formerly an editor of the OpenVX specification, the former chair of the OpenVX Safety-critical subcommittee, and a former member of the OpenGL Safety Critical group and Safety Critical Advisory Panel within Khronos. He stud- ied Physics and Computer Science at York University in the UK, and is a member of the Institute of Physics and a Chartered Physicist.
  • 16. Foreword While preparing to write this preface I was reminded that I have been involved in creating standards for image and vision processing for thirty years, and the work is not yet done. But now, finally, the Khronos Group’s OpenVX API for vision and inferencing is established as the industry’s lead- ing open standard for effectively connecting vision developers to the power of acceleration silicon, enabling a new generation of applications that are visually aware. Why did it take so long to architect an effective vision acceleration API? The earliest efforts took the most obvious approach, simply listing all the potentially useful image and vision operators and creating an API function call for each—done! But not so fast! In hindsight, that approach was too simplistic and suffered from two fatal issues. Firstly, the list of potential func- tions grew unsustainably large, exacerbated by the combinatorial explosion of different input and output parameter types. Those early specifications be- came lists of hundreds (and hundreds) of functions, an impossibly large and diffuse API surface area for silicon vendors to implement effectively. Sec- ondly, sending each function to the hardware in isolation did not provide the silicon implementors enough context to undertake holistic optimiza- tions. Each function call would have to read inputs and write results to default memory positions, with no way to know what operations had gone before and what was about to follow, leaving huge amounts of potential optimizations on the table. OpenVX’s key innovation is enabling application developers to holis- tically describe the complete flow of vision operators in their application as a graph connecting a network of nodes, each node defining a vision function from a carefully curated menu. Handing the complete graph to the silicon enables a groundbreaking level of optimization, for example, avoiding memory round trips or slicing and dicing images to fit in cache memory. Best of all, the graph description is high-level enough to be hard- ware agnostic, so each processor vendor can optimize the graph; however, they wish to execute optimally on their accelerator architecture, and they have all the information they need to do so. But that is not all. An additional key ingredient in OpenVX’s success derives from Khronos’ long expertise in creating specifications that are carefully crafted, and conformance tested, to enable a vibrant ecosystem xiii
  • 17. xiv Foreword containing multiple OpenVX implementations, each optimized for a par- ticular silicon processor while still providing application portability. This is directly analogous to GPU vendors shipping highly optimized 3D drivers for their silicon. It is in contrast to opensource projects where a single li- brary implementation is ported across many platforms—and so may be less optimized than dedicated out-of-the-box vendor drivers. It is a testament to the strength of OpenVX’s underlying design that while early OpenVX implementations were gaining their first users, deep learning burst onto the scene, providing a new and powerful technique for solving complex pattern-matching and image recognition problems. OpenVX was able to leverage its extensible architecture to rapidly in- corporate neural network inferencing into its graph description through a new tensor data type and associated operations. Today, OpenVX provides a uniquely powerful framework that can mix and combine both traditional image operators and hardware accelerated inferencing. Now here we are in 2020, and we have the recently released OpenVX 1.3, which both consolidates proven extensions into the core specifica- tion and provides significant vertical market-targeted deployment flexibility through feature sets. OpenVX now even enables accelerated extensions to be integrated into an application’s graph—an elegant solution to the “100s of functions” problem that plagued the early attempts at vision library stan- dardization. Optimized OpenVX implementations are shipping from multiple hard- ware vendors and routinely delivering superior performance to hand- crafted vision processing code with far great portability and significantly reduced development costs. Strong open standards such as OpenVX result from a wide need for a proven technology in the industry, and in the long view, an open standard that is not controlled by, or dependent on, any single company can often be the thread of continuity for industry forward progress as technologies, platforms, and market positions swirl and evolve. Open standards need the passion, patience, and participation of those that understand their value. OpenVX has been fortunate indeed in benefitting from the passion and patience of the authors of this book, that it has been my pleasure and honor to have worked with, as they have played a central role in creating the OpenVX ecosystem and continue to nurture and evolve this key vision acceleration standard into the future. The thirty-year journey continues. Neil Trevett, President of the Khronos Group
  • 18. Acknowledgments We thank all of our current and former colleagues at the OpenVX com- mittee for their hard and productive work on the standard. This standard would not be possible without Khronos President Neil Trevett, who guided and inspired us, and whose wonderful sense of humor saved us many times when the group was stuck trying to reach a consensus. The anonymous book reviewers provided useful comments on the book structure, and we want to thank Dmitry Matveev and Jesse Villarreal for the feedback on the contents. Our special thanks go to Susheel Gautam, who was the editor of the OpenVX 1.0 and 1.1 specifications, Erik Rainey, who was the editor of OpenVX 1.0 specification and the developer of the first version of the OpenVX sample implementation, and Kari Pulli, who helped to create the standard and was instrumental in promoting it. We are also very grateful to our families, who tolerated us spending long hours on weekends writing this book. xv
  • 19. CHAPTER 1 Introduction Contents 1.1. What is OpenVX and why do we need it? 2 1.2. Portability 4 1.3. OpenVX data objects 5 1.3.1 Opaque memory model 5 1.3.2 Object attributes 6 1.4. Graph API 6 1.5. Virtual objects 8 1.6. Deep neural networks 9 1.7. Immediate mode API 9 1.8. OpenVX vs. OpenCV and OpenCL 10 1.9. OpenVX versions 12 1.10. Prerequisites 12 1.11. Code samples 12 OpenVX1 [1] is an Application Programming Interface (API) that was cre- ated to make computer vision programs run faster on mobile and embedded devices. It is different from other computer vision libraries, because from the very beginning it was designed as a Hardware Abstraction Layer (HAL) that helps software run efficiently on a wide range of hardware platforms. OpenVX is developed by the OpenVX committee, which is a part of the Khronos Group [2]. Khronos is an open and nonprofit consortium; any company or individual can become its member and participate in the de- velopment of OpenVX and other standards, including OpenGL, Vulkan, and WebGL. OpenVX is royalty free, but at the same time, it is developed by the industry: some of the world largest silicon vendors are members of the OpenVX committee. If you are impatient to get started with OpenVX, you might want to skip to the next chapter. The introduction discusses the challenges of run- ning computer vision algorithms in real-time that OpenVX attempts to solve. It will help you get familiar with the OpenVX high-level concepts, such as objects with opaque memory model and the OpenVX Graph. 1 OpenVX is a Khronos Group trademark. OpenVX Programming Guide https://doi.org/10.1016/B978-0-12-816425-9.00007-3 Copyright © 2020 Elsevier Inc. All rights reserved. 1
  • 20. 2 OpenVX Programming Guide 1.1 What is OpenVX and why do we need it? Computer vision can be defined as extracting high-level information from images and video. Nowadays it is in the process of changing many in- dustries, including automotive (Advanced Driver Assistance Systems, self- driving cars), agriculture and logistics (robotics), surveillance and banking (face recognition), and many more. A significant part of these scenarios require a computer vision algorithm to run in real time on a low-power embedded hardware. A pedestrian detection algorithm running in a car has to process each input frame; a delay makes a car less safe. For example, in a car moving at 65 miles per hour, each skipped frame adds about 3 feet to the braking distance. A robot that makes decisions slowly can become a bottleneck in the manufacturing process. AR and VR glasses that track their position slower than 30 frames per second cause motion sickness for many users. Solving a practical computer vision problem in real time is usually a challenging task. The algorithms are much more complicated than running a single linear filter over an image, and image resolution is high enough to cause a bottleneck in a memory bus. In many cases, significant speedups can be reached by choosing the right algorithm. For example, the Viola–Jones face detector algorithm [3] enables detection of human faces in real time on relatively low-power hardware. The FAST [4] feature detector and BRIEF [5] and ORB [6] descriptors allow quick generation of features for tracking. However, in the most of cases, it is not possible to achieve the required performance by just algorithmic optimizations. The concept of optimizing a computer vision pipeline for specific hard- ware is not new to the community, to say the least. One of the first major efforts in this direction was done by Intel, which released the first version of OpenCV library [7] in 2000 along with the optimized layer for Intel CPUs. Other hardware vendors provided their libraries too (see, e.g., [8–10]), and others followed with developing dedicated hardware for computer vision processing, such as the Mobileye solution for automotive [11]. NVIDIA GPUs with their high level of parallelism, and a wide bandwidth memory bus enabled processing of deep learning algorithms that, at the time of writ- ing this book, are the best-known methods of solving a range of computer vision tasks, from segmentation to object recognition. The computer vision applications on mobile/embedded platforms dif- fer from server-based applications: real-time operation is critical. If a web image search algorithm keeps a user waiting, then it is still useful, and we can throw in more servers to make it faster. This is not possible for
  • 21. Introduction 3 a car, where many computer vision tasks (pedestrian detection, forward car collision warning, lane departure warning, traffic sign recognition, driver monitoring) have to work in real time on a relatively low-power hardware. In this context, computer vision optimization for mobile/embedded appli- cations is much more important than for server applications. This is why OpenVX is focused on mobile and embedded platforms, although it can be efficiently implemented for the cloud too. Low-power mobile and embedded architectures typically are heteroge- neous, consisting of a CPU and a set of accelerators dedicated to solving a specific compute intense problem, such as 3D graphics rendering, video coding, digital signal processing, and so on. There are multiple challenges for computer vision developers who want to run their algorithms on such a system. A CPU is too slow to run compute- and data-intense algorithms such as pedestrian detection in real time. One of the reasons is that a mem- ory bus has a relatively low throughput, which limits multicore processing. Executing code on GPU and DSP can help, but writing code that will employ such processing units efficiently across multiple vendors is next to impossible. GPUs and DSPs have no standard memory model. Some of them share RAM with CPU, and some have their own memory with a sub- stantial latency for data input/output. There are ways to write code that will execute on both CPU and GPU, such as [12]. However, developers want higher-level abstraction, which may be implemented over low-level API, such as OpenCL, or coded directly in the hardware. Chapter 13 discusses how OpenVX and OpenCL code can coexist and benefit from each other. Also, there are architectures that do not have full IEEE 754 floating point calculations support (required in the current version of OpenCL); they im- plement fixed-point arithmetic instead (where a real number is represented with a fixed amount of digits after the radix point). Finding a balance be- tween precision and speed on such architectures is a serious challenge, and we have to tune an algorithm for such platforms. Computer vision is too important to allow such an overhead, and there has to be a way to write an algorithm that will run efficiently on a wide variety of accelerators, imple- mented and tuned for a specific mobile or embedded platform. This is the problem solved by OpenVX. The OpenVX library is usually developed, optimized, and shipped by silicon vendors, just like a 3D driver for a GPU. It has a graph API, which is convenient to use and efficient for executing on heterogeneous platforms. One of the most important requirements for this API is its portability across platforms.
  • 22. 4 OpenVX Programming Guide 1.2 Portability OpenVX is the API that allows a computer vision developer to write a program once and have it efficiently executed on hardware, assuming that an efficient OpenVX implementation exists for that hardware. A reasonable expectation for this API is that it produces the same results across different platforms. But what does “same results” actually mean? How do we test if two implementations of the same function return the same results? There are several ways to answer this question: • Bit-exact tests: the output of an implementation should not differ from the ground truth by a single bit. Such a test is useful to ensure that the results are going to be the same. However, there are many cases where bit-exact tests make no sense. For example, a simple function for correcting camera lens distortion [13] uses floating point calcula- tions, and even if a method to compute an image transformation is well specified, a bit exact requirement may be too strong. For instance, a conformance to IEEE 754 floating point calculation standard may re- sult in a very inefficient implementation on a fixed-point architecture. So, in many cases, it is beneficial to allow for a limited variety in the results. • Tolerance-based comparison: for example, we can require that the output image is not different from the reference implementation output by more than in each pixel and each channel. There is no exact science in choosing the value of the threshold . The higher the threshold, the more space there is for optimizing functions for a specific platform, and the higher the variability of the results across different platforms. Com- puter vision means extracting high-level information from images, and if greater variability in this high-level information prevents solving the problem, then the thresholds are too high. For instance, an image arith- metic operation involved in an image stitching algorithm that results in a high-quality image on one platform and produces a visible stitch line on another should not pass a tolerance-based test. • Algorithmic tests: a useful test to ensure a function returns correct results is to test some properties of this result. For instance, a face de- tector should return an image rectangle that contains a face. A text segmentation algorithm should return areas in an image that contain text. Although algorithmic tests are useful for any computer vision li- brary, they usually are a poor choice for ensuring portability. Whereas a face detector returns a rectangle with a face, the algorithm processing this rectangle can work differently depending on the size and position
  • 23. Introduction 5 of the rectangle. If the rectangle parameters vary across platforms, then the overall results may also depend on the platform. As we can see, portability is a fine balance between precision and op- timization. A set of tests that check for portability called conformance tests is an essential part of any cross-platform standard. Conformance tests are dif- ferent from algorithmic tests: their objective is not only to check whether a function returns results according to the specification, but also to ensure the results to be consistent across different platforms. This is why conformance tests usually consist of bit-exact and tolerance-based tests, rarely using algo- rithmic tests. OpenVX has its own set of conformance tests [14,15], designed by the OpenVX committee and available to all implementers of the standard. Passing conformance tests is an essential requirement for an implementation to be called “OpenVX.” 1.3 OpenVX data objects OpenVX defines several objects that are needed for common computer vision algorithms. This list includes images, pyramids, matrices, tensors, and several other constructs. All the data objects share some high-level design properties. 1.3.1 Opaque memory model An efficient cross-platform computer vision API should be able to exe- cute the same function on different accelerators. A function implementing a stereo matching algorithm should be computed on a GPU, provided that a GPU is present on the platform and is powerful enough to execute this function faster than a CPU. However, a GPU has its own memory, and executing stereo matching on a GPU will require moving image data to the GPU memory. Giving a user control over memory that stores image data requires exposing a memory model for a wide range of embedded ac- celerator memory models that OpenVX is targeting. This is why the image object in OpenVX is opaque, meaning that a user has no direct access to pixels other than through dedicated OpenVX functions. The same design principle is applied to all OpenVX data objects. This allows an implemen- tation of OpenVX to change data layout and to move data between the CPU memory, called host memory throughout the OpenVX spec, and the accelerator memory. The access to data is provided by a pair of Map/Un- map functions defined for each object, which create a mapping between
  • 24. 6 OpenVX Programming Guide object data and a region in host memory. For example, the functions for accessing image pixels are called vxMapImagePatch and vxUnmapImagePatch. 1.3.2 Object attributes Each object, in addition to the data it holds, has parameters that define its contents. For example, an image is characterized by width and height, as well as its format (the number of color planes, color format, etc.). These parameters are called attributes throughout the spec. Some of the attributes have to be specified when an object is being created and can- not be changed by a user during the object lifetime. Such attributes are referred to as “Read-only” in the spec. Attributes that can be changed are marked as “Read-write.” Values of object attributes can be retrieved with a “query” function, usually named vxQuery[Object], for example, vxQueryImage for the image object. The read-write attributes can be changed with a “set” function, usually named vxSet[Object]Attribute, for example, vxSetImageAttribute for the image object. Appendix A contains the list of all OpenVX data objects along with their attributes. 1.4 Graph API As we discussed before, data objects in OpenVX can be stored in the host memory and accelerator memory. However, moving image data between devices can be time consuming. If we are to run a single image filtering operation, then a GPU may be faster to do the job than a CPU, but the overhead of moving image data between the host and GPU memory may negate all the speedup. Making such decisions requires knowledge about the platform. If we intend to execute an OpenVX-based algorithm on a wide variety of platforms (such as Android phones), all kinds of accelerators available on these platforms, as well as memory and data transfer between accelerators, need to be taken into account. It is practically impossible to build this level of decision making into the code. It is more realistic to expect a manufacturer of a specific hardware compute platform to take into account all this special knowledge in an efficient OpenVX implementation. This is why an OpenVX implementation manages where and when specific functions are going to be executed, and a user has practically no control over it. As a result, OpenVX provides an abstraction level that allows us to write code once and execute it efficiently on many different embedded platforms where the API is implemented.
  • 25. Introduction 7 Figure 1.1 An example of an OpenVX graph. Graph nodes represent functions, and the directed links define input and output data. Since an OpenVX implementation has to make decisions on how to execute each function, we need to provide it with some context in advance, so that efficient data management can be planned before the execution. Letting an implementation know a sequence of functions that we want to call on an input image will allow it to make an informed decision on where to keep image data and when to move it to/from an accelerator. A graph model is used in OpenVX to represent this information. The idea of using graphs to express sequences of image processing functions has been around for a long time (see, e.g., [16]). A program is represented by a Directed Acyclic Graph (DAG), with nodes correspond- ing to functions and oriented links between nodes setting the input and output data, as well as the order of execution. An example of a graph is given in Fig. 1.1. The input color image in the RGB format is converted into the YUV format by the first (leftmost) node, then a Y (intensity) chan- nel is extracted by the second node, and, finally, the camera lens distortion correction is done on the Y channel. If a function has parameters, then they are specified by node attributes, similar to data object attributes. An OpenVX graph is set up in advance, before execution. Once all the nodes and links are set up, the graph is verified, either automatically before execution or with a manual function call. During graph verification time, the OpenVX implementation may prepare for execution, make decisions about which accelerator to use for executing each node, and check for er- rors in the graph. For some platforms such as FPGA, it can also compile a graph into binary code and upload it to an FPGA chip. Once the verifi- cation is complete, the graph is ready to be executed. Note that the same graph can be executed multiple times on different data without reverifica- tion. Changes in a graph such as insertion/deletion of nodes or links can trigger reverification. Changes in some node attributes may also require reverification before the next execution. A list of OpenVX graph nodes representing computer vision functions, along with the references to spe-
  • 26. 8 OpenVX Programming Guide Figure 1.2 An example of an OpenVX graph with virtual images. Graph links with vir- tual images define a sequence of calls, but a user has no access to that data at any time. cific chapters where these functions are discussed and to the code samples, is given in Appendix B. Obviously, not every possible computer vision function can be prede- fined by OpenVX. Users can implement a C function and add it to a graph as a user node. A user node will be executed on a CPU, but it does not prevent using accelerators for user nodes. For instance, Chapter 13 dis- cusses how a user node can be implemented with OpenCL, so that code get executed on a GPU/FPGA/DSP. The concept of user nodes allows a developer to go well beyond the set of computer vision functions that are included in the OpenVX standard. 1.5 Virtual objects To provide an OpenVX implementation more opportunities for optimiza- tion, a user has no access to data during graph execution (other than from user nodes). In the example of Fig. 1.1, once the graph execution is fin- ished, access to all four images (input image, image 1, image 2, and output image) is possible. However, in many cases a user does not need to access temporary data (images 1 and 2), whereas generating it may cause an addi- tional speed and memory overhead. For example, if we did not need images 1 and 2, all three operations could happen in place, pixel by pixel, provid- ing a lot of speedup on platforms with slow memory bus by minimizing the data flow. Examples of such operations include combining subsequent per-pixel image arithmetic operations and applying several linear filters to an image at the same time (so-called filter stacking). If a user does not need specific images, then he can create them as virtual (see Fig. 1.2). Vir- tual images, like regular data objects, connect two graph nodes, but an implementation is not obliged to actually generate data for these images. OpenVX does not require a user to provide image dimensions when cre- ating a virtual image; zeroes can be used instead (see vxCreateVirtualImage
  • 27. Introduction 9 in Chapter 6). It may be useful when you have a large graph with many images of different sizes (e.g., when using pyramids, see Chapter 8), but the authors recommend to use actual image sizes where possible, as it is less error prone. All other OpenVX data objects, such as pyramids and histograms, can also be virtual. It is reasonable to expect that Map/Unmap function calls on virtual objects fail, but OpenVX requires this only outside of graph execution. If Map/Unmap are called inside a graph (from a user node), then the calls will be successful. So, a user of OpenVX has to be aware that inserting a user node in a sequence of nodes connected with virtual images may prevent certain optimizations. 1.6 Deep neural networks Deep neural networks have recently become the standard tool for solving a variety of computer vision problems. Whereas training a neural network is outside the OpenVX scope, importing a pretrained network and run- ning inference on it is an important part of the OpenVX functionality. The concept of the Graph API of nodes representing functions and links rep- resenting data is very convenient for implementing deep neural networks with OpenVX. In fact, each neural network unit can be represented as a graph node. OpenVX has a special data type representing tensors to pro- vide data exchange between these nodes, and the nodes themselves are implemented in the OpenVX Neural Network Extension [17]. Another way to import a neural network into OpenVX is by using the OpenVX Kernel Import Extension [18]. The Kernel Import Extension can take a pretrained network model and load it into OpenVX as a single node. One of the data formats that can be used is Neural Network Exchange Format (NNEF) [20], the standard also developed by the Khronos Group. See Chapter 10 for details on how to import a pretrained neural network into OpenVX. 1.7 Immediate mode API There are cases where the Graph API in its existing form does not provide an efficient implementation of a computer vision algorithm. For example, a sequence of quick custom operations on small blocks of pixels intermit- tent with standard computer vision functions, defined with a Graph API, will require a user node for each custom operation. Each user node will have to map and unmap an image to/from host memory, causing a signif-
  • 28. 10 OpenVX Programming Guide icant overhead. This is why OpenVX, on top of the Graph API, also has the Immediate Mode API. Each immediate mode function replicates a graph node, with a few exceptions. The behavior of an immediate mode function is defined by the corresponding single node graph. Each immediate mode function name is prefixed with “vxu”, to distinguish them from Graph API functions, prefixed with “vx”. An immediate mode API allows us to write a computer vision algorithm without a graph and has small overhead for custom data processing. 1.8 OpenVX vs. OpenCV and OpenCL OpenVX is not the first framework optimized for computer vision. OpenCV (Open Source Computer Vision Library) [21] is the de facto standard in this area. It provides a lot of useful functions, and many of them are optimized for specific platforms, including x86, ARM CPUs, and se- lected GPUs. Also, OpenCL [12], a generic programming framework, can be used to develop computer vision algorithms that can run on discrete GPUs. So why do we need another computer vision platform? OpenCV is developed and maintained by the community, with the pri- mary focus on providing necessary computer vision functionality. Many functions are optimized for a subset of platforms, and hardware vendors provide their own optimizations for their platforms (see, e.g., [22–24]). However, the wide scope of the library makes the optimization of all the functions for multiple embedded platforms next to impossible. Also, OpenCV uses algorithmic tests to check for function correctness, so the results across platforms may be different (although functionally correct). In contrast, OpenVX has a much smaller scope, making it possible to optimize for a wide range of platforms. Also, OpenVX conformance tests make sure the results to be similar across different platforms. In fact, these frameworks are complimentary: we can use the OpenVX graph for functions that are defined by OpenVX and then add more functionality as user nodes, based on OpenCV. A full comparison between OpenCV and OpenVX is given in the Table 1.1. OpenCL is a general-purpose programming language that allows us to write code for heterogeneous systems. OpenCL existing requirement for full IEEE 754 floating point standard compliance2 and its explicit memory 2 This requirement may be relaxed in future.
  • 29. Introduction 11 Table 1.1 OpenVX vs. OpenCV∗. OpenCV OpenVX Implementation Community-driven open-source library Callable API implemented, optimized, and shipped by hardware vendors Scope 100s of imaging and vision functions, multiple camera APIs/interfaces Tight focus on dozens of core hardware-accelerated functions plus extensions and accelerated custom nodes uses external camera drivers Conformance Extensive OpenCV Test Suite but no formal Adopters program Implementations must pass Khronos Conformance Test Suite to use trademark IP Protection None. Source code licensed under BSD. Some modules require royalties/licensing Protected under Khronos IP Framework – Khronos members agree not to assert patents against API when used in Conformant implementations Acceleration OpenCV 3.0 Transparent API (or T-API) enables function offload to OpenCL devices Implementation free to use any underlying API such as OpenCL. Can use OpenCL for Custom Nodes Efficiency OpenCV 4.0 G-API graph model for some filters, arithmetic/binary operations, and well-defined geometrical transformations Graph-based execution of all Nodes. Optimizable computation and data transfer Inferencing Deep Neural Network module to construct networks from layers for forward pass computations only. Import from ONNX, TensorFlow, Torch, Caffe Neural Network layers and operations represented directly in the OpenVX Graph. NNEF direct import, ONNX through NNEF convertor ∗ Courtesy of Neil Trevett, Khronos Group model prevent OpenVX to be implemented only using OpenCL. However, OpenCL can be used to efficiently implement a user node to run on a GPU (see Chapter 13 for more information). Also, OpenCV T-API can be used to run a computer vision algorithm implemented in OpenVX on an accelerator through OpenCL. A full comparison between OpenCL and OpenVX is given in the Table 1.2.
  • 30. 12 OpenVX Programming Guide Table 1.2 OpenVX vs. OpenCL∗. OpenCL OpenVX Use case General heterogeneous programming Domain targeted vision processing Ease of use General-purpose math libraries with no built-in vision functions Fully implemented vision operators and framework “out of the box” Architecture Language-based – needs online compilation Library-based – no online compiler required Target hardware “Exposed” architected memory model – can impact performance portability Abstracted node and memory model – diverse implementations can be optimized for power and performance Precision Full IEEE floating point mandated Minimal floating point requirements – optimized for vision operators ∗ Courtesy of Neil Trevett, Khronos Group 1.9 OpenVX versions This revision of the book was mostly written for OpenVX version 1.2. Version 1.3 was released in September 2019, and Chapter 15 provides in- formation on the new features introduced in OpenVX 1.3. 1.10 Prerequisites We expect a reader to be familiar with the C programming language, as the API and all of the examples are in C. Also, we assume that a reader has experience with computer vision, and so we will not go into explana- tions about what a specific algorithm does or how it works. Here are some useful references on the subject [13, 25, 26, 27]. Having experience with OpenCV [21,28] can also be useful. 1.11 Code samples Throughout the book, we often make use of code samples that show how to work with specific OpenVX functions. The samples are available for down- load from https://github.com/rgiduthuri/openvx_tutorial [29] under MIT license. The input data for them is located at https://www.dropbox.com/ sh/urzzs64a85tqf3d/AADxdIEer_tjHFGzBifZWhsLa. The samples have
  • 31. Introduction 13 been tested against the OpenVX sample implementation [30] available for download from https://github.com/KhronosGroup/OpenVX-sample- impl. See the beginning of Chapter 2 for the details on how to build and run the samples.
  • 32. CHAPTER 2 Build your first OpenVX program Contents 2.1. First things first 15 2.1.1 How to get the OpenVX sample implementation 16 2.1.2 Building the examples 16 2.2. Immediate mode 17 2.2.1 Our first very simple example in immediate mode 17 2.3. Graph mode 22 2.3.1 Creating the graph 22 2.3.2 Connecting to parameters and running the graph 24 2.3.3 Running example 4 25 2.3.4 Code for example 4 25 2.4. Image input and output 30 2.4.1 Converting to and from portable pixmap format 30 2.4.2 Other libraries for input and output of images 31 2.4.3 Image processing exercises 31 2.4.4 Answers, or at least hints, for the exercises 32 2.1 First things first If you are going to build an OpenVX program, you will need a few things: • A computer. In this first chapter, we assume that you are using a Linux- based machine; in general, this is the easiest option, and what you read in this chapter has been tested on a machine running Ubuntu 16.04. • A text editor to write your program. You probably have a favorite, whether it is something simple like Vi, Nano, or Scite, or more com- plex like Emacs or Eclipse. • A toolchain. For the examples, we assume GCC and give code in C99 (ISO/IEC 9899:1999, ref TBD). This should be a suitably low com- mon denominator. You will also need CMake to build the examples. • Some examples depend on 3rd party libraries: OpenCV [21], vxa [31], OpenVX-SC-Plus [32], and LAPACK [33]. • Our examples, downloaded from https://github.com/rgiduthuri/ openvx_tutorial [29]. • Input data for the examples, downloaded from https://www.dropbox. com/sh/urzzs64a85tqf3d/AADxdIEer_tjHFGzBifZWhsLa. OpenVX Programming Guide https://doi.org/10.1016/B978-0-12-816425-9.00008-5 Copyright © 2020 Elsevier Inc. All rights reserved. 15
  • 33. 16 OpenVX Programming Guide • Last but not least, a copy of OpenVX. We use the sample implementa- tion from Khronos, but of course you may have another implementa- tion from your favorite IP supplier. 2.1.1 How to get the OpenVX sample implementation Both the OpenVX Specification and a sample implementation may be obtained from https://www.khronos.org/registry/OpenVX/. The sample implementation of OpenVX V1.2 is a tar.bz2 file, which should be unar- chived to a suitable folder, for example, your home folder, and then built using the instructions in the file “README”. If you opt to install OpenVX on a Linux system, then the libraries will be available in /us- r/lib/, and the include file path for building the examples in this book will be ˜/OpenVX_sample/include, assuming that you unzipped the archive into your home folder. 2.1.2 Building the examples Assuming that you unzipped the sample implementation into your home folder, then built and installed it, the following commands should be suffi- cient to build the examples for this chapter. Note that the first three exam- ples need the immediate-mode library (vxu) in addition to the OpenVX library. $ mkdir build cd build $ gcc ../book_sample/example1/example1.c -I ~/OpenVX_sample/include -l OpenVX -l vxu -o example1 $ gcc ../book_sample/example2/example2.c -I ~/OpenVX_sample/include -l OpenVX -l vxu -o example2 $ gcc ../book_sample/example3/example3.c -I ~/OpenVX_sample/include -l OpenVX -l vxu -o example3 $ gcc ../book_sample/example4/example4.c -I ~/OpenVX_sample/include -l OpenVX -o example4 $ gcc ../book_sample/example4/example4a.c ../book_samples/ppm-io/writeImage.c -I ~/OpenVX_sample/include -I ../book_samples/ppm-io/ -l OpenVX -o example4a $ gcc ../book_sample/example4/changeImage.c ../book_samples/ppm-io/readImage.c ../book_samples/ppm-io/writeImage.c -I ~/OpenVX_sample/include -I ../book_samples/ppm-io/ -l OpenVX -o changeImage To build all examples, you will need to first install the prerequisites (OpenCV, vxa, and LAPACK) and then use CMake to build the examples.
  • 34. Build your first OpenVX program 17 The following commands assume that your current directory is the folder “book_samples” with the samples: $ cd ../ $ mkdir build $ cd build $ cmake -G Unix Makefiles ../book_samples $ make 2.2 Immediate mode There are two ways to use OpenVX, “immediate mode” and “graph mode”. The immediate mode is pretty straightforward, and is there re- ally only for quick prototyping with the OpenVX functions, so that people who are used to using traditional APIs like OpenCV can get a quick flavor of the sort of functionality that is available in OpenVX. But the real power is in the “graph mode”, because this allows implementations to do a lot of optimization. The graph mode is intended to address the main use case of OpenVX, which is when you want to apply the same processing repeatedly to different data, for example, a camera feed. The immediate mode does what you might expect—you call a function and immediately get the results. For example, you can do a pixel-by-pixel multiplication of two images and get the result in a third image simply by calling one function. We will use the immediate mode for our first example. 2.2.1 Our first very simple example in immediate mode The code for this example is in example1/example1.c. In outline, what it does is: • Create an OpenVX context • Create an image that is a white rectangle on a black background • Locate the corners in the image, using the Fast Corners algorithm with and without nonmaximum suppression • Display the results The first thing we have in the main() function is the line vx_context context = vxCreateContext(); This line creates the OpenVX context, which is the first thing we have to do in any OpenVX program. Without this, there is nothing else we can do
  • 35. 18 OpenVX Programming Guide with OpenVX. Similarly, when we have finished with OpenVX, we release the context—in our example, this is at the end of main(): vxReleaseContext(context); This tells the OpenVX infrastructure that we have finished with the con- text, and it can go ahead and destroy it, together with anything else inside it that we have not already destroyed. The second thing we do after calling vxCreateContext is checking that the context is OK and there were no any errors. In general, you can check any type of vx_reference by calling the function vxGetStatus() on it; if the result is VX_SUCCESS then it is OK to use it in further functions. If the result is not equal to this value, then probably it is a good time to stop. We have made a useful function errorCheck() that bails out with exit(1) if the status is nonzero—note that VX_SUCCESS is defined as being zero: void errorCheck(vx_context *context_p, vx_status status, const char *message) { if (status) { puts(ERROR! ); puts(message); vxReleaseContext(context_p); exit(1); } } We use this in the main function right after creating the context: errorCheck(context, vxGetStatus((vx_reference)context), Could not create a vx_contextn); Let us take a look at the second function we have defined to create our white rectangle on a black background: vx_image makeInputImage(vx_context context) { vx_image image = vxCreateImage(context, 1024U, 768U, VX_DF_IMAGE_U8); vx_rectangle_t rect = { .start_x = 256, .start_y = 192, .end_x=768, .end_y = 576 };
  • 36. Build your first OpenVX program 19 if (VX_SUCCESS == vxGetStatus((vx_reference)image)) { vx_image roi = vxCreateImageFromROI(image, rect); vx_pixel_value_t pixel_white, pixel_black; pixel_white.U8 = 255; pixel_black.U8 = 0; if (VX_SUCCESS == vxGetStatus((vx_reference)roi) VX_SUCCESS == vxSetImagePixelValues(image, pixel_black) VX_SUCCESS == vxSetImagePixelValues(roi, pixel_white)) vxReleaseImage(roi); else vxReleaseImage(image); } return image; } We make our black background by filling all of the image and then a white rectangle by filling an ROI (region of interest) in this background. When we have finished with the ROI, we release it as we need not it any more. Note that we do not touch the image or the ROI further if we get an error on creating them, and if we fail to create the ROI or copy the data, then we release the image and return a NULL pointer. Back in the main() function, we have to test the image after we have created it: vx_image image1 = makeInputImage(context); errorCheck(context, vxGetStatus((vx_reference)image1), Could not create image); Now we still need some other parameter declarations before we can call the vxu functions to find the corners. You will find descriptions of the parameters for vxuFastCorners in the documentation for OpenVX. We also allocate some host memory for an array of key points as we will need somewhere to get our results back from OpenVX so that we can display them: vx_float32 strength_thresh_value = 128.0; vx_scalar strength_thresh = vxCreateScalar(context, VX_TYPE_FLOAT32, strength_thresh_value); vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100); vx_array corners1 = vxCreateArray(context, VX_TYPE_KEYPOINT, 100); vx_size num_corners_value = 0;
  • 37. 20 OpenVX Programming Guide vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE, num_corners_value); vx_scalar num_corners1 = vxCreateScalar(context, VX_TYPE_SIZE, num_corners_value); vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t)); Now we have to check that everything has been created OK, including kp, which should not be NULL: errorCheck(context, kp == NULL || vxGetStatus((vx_reference)strength_thresh) || vxGetStatus((vx_reference)corners) || vxGetStatus((vx_reference)num_corners) || vxGetStatus((vx_reference)corners1) || vxGetStatus((vx_reference)num_corners1), Could not create parameters for FastCorners); Then we can call the functions to do the actual work, once with non- maximum suppression and once without: errorCheck(context, vxuFastCorners(context, image1, strength_thresh, vx_true_e, corners, num_corners), Fast Corners function failed); errorCheck(context, vxuFastCorners(context, image1, strength_thresh, vx_false_e, corners1, num_corners1), Fast Corners function failed); The last part of the example reads the output arrays and shows the results. You can clearly see the effect of nonmaximum suppression; in the following examples, we will use nonmaximum suppression, so that we see just the corners we expect. In example 2, we use vxuWarpAffine to rotate our input image through 90 degrees and then vxuOr to logically OR the two images together, creating a white cross on a black background, and of course we see 12 corners. The affine transform is a rotation where each output x and y pixel is taken from pixels at addresses given by a linear combination of the input addresses; if the input address is not integral, then interpolation is performed as specified. The coefficients given in the code as the matrix_values simply swap x and y with no translation (no offset); then this is a 90-degree rotation. Extracts from example2/example2.c: vx_matrix warp_matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32, 2U, 3U);
  • 38. Build your first OpenVX program 21 vx_float32 matrix_values[3][2] = { /* Rotate through 90 degrees */ {0.0, 1.0}, /* x coefficients */ {1.0, 0.0}, /* y coefficients */ {0.0, 0.0} /* offsets */ }; errorCheck(context, vxCopyMatrix(warp_matrix, matrix_values, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST), Could not initialize the matrix); errorCheck(context, /* Now image2 set to image 1 rotated */ vxuWarpAffine(context, image1, warp_matrix, VX_INTERPOLATION_NEAREST_NEIGHBOR, image2) || /* image3 set to logical OR of images 1 and 2 */ vxuOr(context, image1, image2, image3) || /*And now count the corners */ vxuFastCorners(context, image3, strength_thresh, vx_true_e, corners, num_corners), Image functions failed); In example 3, we take this further, using the Sobel, Magnitude, and Di- late filters to replace the white cross with the outline of a white cross, and the corner count doubles as both internal and external corners are counted. The Sobel filter is an edge detection filter, and applying it to the cross gives us two output images, the gradient in the x direction and the gradient in the y direction. Taking the magnitude of the x and y images gives us a solid line of single pixels outlining the cross. Note that the vxuMagnitude function takes two 16-bit signed images that are the output of vxuSobel and produces a 16-bit signed image as output. However, most other OpenVX functions only operate on 8-bit unsigned images, so we use the vxuConvertDepth func- tion to produce such an image, with VX_CONVERT_POLICY_SATURATE specifying that values below zero should be set to zero, and values above 255 should be set to 255. Finally, the vxuDilate3x3 function applies a dilation filter, mak- ing the thin outline more substantial. So far, we have imagined how the output will actually look, but if you would like a sneak preview, then look at Fig. 2.2 later in the chapter. From example3/example3.c: /* Now image2 set to image 1 rotated */ vx_status status = vxuWarpAffine(context, image1, warp_matrix, VX_INTERPOLATION_NEAREST_NEIGHBOR, image2); /* image3 set to logical OR of images 1 and 2 and then processed as described above */
  • 39. 22 OpenVX Programming Guide errorCheck(context, vxuOr(context, image1, image2, image3) || vxuSobel3x3(context, image3, grad_x, grad_y) || vxuMagnitude(context, grad_x, grad_y, magnitude) || vxuConvertDepth(context, magnitude, converted, VX_CONVERT_POLICY_SATURATE, 1) || vxuDilate3x3(context, converted, dilated) || /* And now count the corners */ vxuFastCorners(context, dilated, strength_thresh, vx_true_e, corners, num_corners), Image functions failed); In example 3, you see that we are using a lot of images! In fact, most of these images really need not be retained, since we are not interested in the intermediate data. The only image of any interest is actually the input image that we create. After that, we only use the output from the final corner detector. 2.3 Graph mode Let us now have a look at graph mode. Example 4 is a graph-mode version of example 3, and comparing the two will help us see how it works. To make it easier to understand the various things going on, example 4 (see the file example4/example4.c) introduces a number of changes: • The function makeInputImage() now takes two more parameters, the half-width and half-height of the rectangle in the image, so it is easy to make different images. • A new function makeTestGraph() creates a graph that implements the functionality of the vxu functions that were called in main() in exam- ple 3. • Similarly, a new function showResults() runs the graph with an image and displays the results. • A new function getGraphParameter() is a simple wrapper to make some of the OpenVX library functions more accessible. • The new main() is a lot simpler, and it runs the graph with two different images. Now let us look at things in more detail. 2.3.1 Creating the graph In the function makeTestGraph(), we make extensive use of virtual images— these are images where we do not want either to read or write the data, and
  • 40. Build your first OpenVX program 23 we do not care really how the framework decides to implement them. We use them to connect together processing nodes in the graph, just to inform the framework about the topology. Note that is does not matter in which order nodes are added to the graph (here we do it backward), because the topology is totally defined by the connecting edges, that is, the data objects. The graph constructed in makeTestGraph() can be visualized as in Fig. 2.1. For clarity, some parameters (edges) of the graph are excluded, but all the processing nodes are there. As you can see, there is opportunity for parallel execution. Since the data objects are virtual, the implementation is free to perform optimization by merging nodes together. Figure 2.1 Illustration of the graph created in example 4. Graph nodes represent func- tions, and the directed links define input and output data. The input image we create is not virtual, but like the virtual images and the other interim objects, and even those objects containing the output data, we release our reference to it before exiting the function. How then do we introduce an input or see the results? The answer is that we use graph parameters. Graph parameters in OpenVX give you a simple mechanism for changing input or output ob- jects in a graph once it has been made, and it is a mechanism that allows the actual content of the graph (the nodes and edges) to remain hidden. A graph parameter is associated with the input or output of one node only, so if you want an object to be an input for several nodes, then you can use a Copy node to isolate the input graph parameter. Copy nodes simply copy their input to their output; this is of course a logical or conceptual opera- tion, and the implementation will just “optimize away” the Copy node if its output is a virtual object, so there is no actual overhead when executing the graph. Note that our input image in example 3 was an input to two vxu func- tions, vxuWarpAffine() and vxuOr(), and similarly when we create a graph
  • 41. 24 OpenVX Programming Guide implementing this same functionality, we feed the same input image into two nodes. It is inconvenient to parameterize two images in this way; we would like just the one input to go to two places. So we introduce the useful Copy node; the output of this feeds both vxWarpAffineNode() and vxOrNode(), and we are able to create just one graph parameter associated with the input of the Copy node: vxAddParameterToGraph(graph, vxGetParameterByIndex(vxCopyNode(graph, (vx_reference)input, (vx_reference)imagesU8[0]), 0)); It is important to note that graph parameters are added in order; since we added this one first, it will be identified with an index value of zero, and the output parameters that we add afterward will have indices 1 and 2. The assumption is that the creator of the graph will document it correctly so that anyone using it will know which parameter is which. Note that we do not have much error checking in the graph creation function. This is because errors in creation of all the objects and nodes will be caught when the graph is processed; if we need more detail, then we can turn on logging and see some diagnostic output from the framework without having to test each function call result. 2.3.2 Connecting to parameters and running the graph So, have a look at main() in the file example4/example4.c; it is also re- produced later in this section. Here we create the graph by calling the makeTestGraph() function and a couple of images, just rectangles of differing sizes, so we can illustrate calling the graph with a different input parame- ter. Running the graph and displaying the results is the work of our new function showResults(), which takes a vx_graph and a vx_image as parameters. It sets the image as the input parameter to the graph, which as you will remember was given the index 0 by adding it to the graph first: vxSetGraphParameterByIndex(graph, 0, (vx_reference)image); Next, we run the graph using the function vxProcessGraph(). The first time it is run, vxVerifyGraph() will be called internally, and this is the point at which any errors will be picked up. At this point the implementation may do any other processing it deems necessary. Some implementations may aggressively optimize the graph, taking advantage of any virtual objects so that kernels can be coalesced and data movement reduced to a minimum. At the present time the Khronos Sample Implementation does not do this, but implementations from hardware vendors will do so.
  • 42. Build your first OpenVX program 25 The second and third graph parameters are the array of corners and number of corners. These are extracted from the graph using our handy function getGraphParameter() and then processed just as in the previous ex- ample 3. It is worth having a little look at getGraphParameter() as it illustrates a use of one of the vxQueryXXX() functions. 2.3.3 Running example 4 Example 4 gives two sets of output. The first should be the same as for example 3, that is, 24 corners, as the image is identical. The second set of results should give just 8 corners, since the input image is a square, which when rotated 90 degrees and superimposed upon itself is still a square, so you will see 8 corners, 4 external and 4 internal. Figure 2.2 The output images from example 4a. The images were created using the same graph with different input images. 2.3.4 Code for example 4 void errorCheck(vx_context *context_p, vx_status status, const char *message) { if (status) { puts(ERROR! ); puts(message); vxReleaseContext(context_p); exit(1); } } vx_image makeInputImage(vx_context context, vx_uint32 width, vx_uint32 height)
  • 43. 26 OpenVX Programming Guide { vx_image image = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8); if (width 48) width = 48; if (height 48) height = 48; vx_rectangle_t rect = { .start_x = 50 - width, .start_y = 50 - height, .end_x = 50 + width, .end_y = 50 + height }; if (VX_SUCCESS == vxGetStatus((vx_reference)image)) { vx_image roi = vxCreateImageFromROI(image, rect); vx_pixel_value_t pixel_white, pixel_black; pixel_white.U8 = 255; pixel_black.U8 = 0; if (VX_SUCCESS == vxGetStatus((vx_reference)roi) VX_SUCCESS == vxSetImagePixelValues(image, pixel_black) VX_SUCCESS == vxSetImagePixelValues(roi, pixel_white)) vxReleaseImage(roi); else vxReleaseImage(image); } return image; } vx_graph makeTestGraph(vx_context context) { vx_graph graph = vxCreateGraph(context); int i; vx_image imagesU8[5], imagesS16[3]; vx_image input = vxCreateImage(context, 100U, 100U, VX_DF_IMAGE_U8); for (i = 0; i 5; ++i) imagesU8[i] = vxCreateVirtualImage(graph, 100, 100, VX_DF_IMAGE_U8); for (i = 0; i 3; ++i) imagesS16[i] = vxCreateVirtualImage(graph, 0, 0, VX_DF_IMAGE_VIRT);
  • 44. Build your first OpenVX program 27 vx_matrix warp_matrix = vxCreateMatrix(context, VX_TYPE_FLOAT32, 2U, 3U); vx_float32 matrix_values[6] = {0.0, 1.0, 1.0, 0.0, 0.0, 0.0 }; /* Rotate through 90 degrees */ vx_float32 strength_thresh_value = 128.0; vx_scalar strength_thresh = vxCreateScalar(context, VX_TYPE_FLOAT32, strength_thresh_value); vx_array corners = vxCreateArray(context, VX_TYPE_KEYPOINT, 100); vx_size num_corners_value = 0; vx_int32 shift_value = 1; vx_scalar num_corners = vxCreateScalar(context, VX_TYPE_SIZE, num_corners_value); vx_scalar shift = vxCreateScalar(context, VX_TYPE_INT32, shift_value); vxCopyMatrix(warp_matrix, matrix_values, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST); /* Create the nodes to do the processing, order of creation is not important */ vx_node last_node = vxFastCornersNode(graph, imagesU8[4], strength_thresh, vx_true_e, corners, num_corners); vxDilate3x3Node(graph, imagesU8[3], imagesU8[4]); vxConvertDepthNode(graph, imagesS16[2], imagesU8[3], VX_CONVERT_POLICY_SATURATE, shift); vxMagnitudeNode(graph, imagesS16[0], imagesS16[1], imagesS16[2]); vxSobel3x3Node(graph, imagesU8[2], imagesS16[0], imagesS16[1]); vxOrNode(graph, imagesU8[0], imagesU8[1], imagesU8[2]); vxWarpAffineNode(graph, imagesU8[0], warp_matrix, VX_INTERPOLATION_NEAREST_NEIGHBOR, imagesU8[1]); /* Setup input parameter using a Copy node */ vxAddParameterToGraph(graph, vxGetParameterByIndex(vxCopyNode(graph, (vx_reference)input, (vx_reference)imagesU8[0]), 0)); /* Setup the output parameters from the last node */ vxAddParameterToGraph(graph, vxGetParameterByIndex(last_node, 3)); /* array of corners */ vxAddParameterToGraph(graph, vxGetParameterByIndex(last_node, 4)); /* number of corners */
  • 45. 28 OpenVX Programming Guide /* Release resources */ vxReleaseImage(input); for (i = 0; i 5; ++i) vxReleaseImage(imagesU8[i]); for (i = 0; i 3; ++i) vxReleaseImage(imagesS16[i]); vxReleaseMatrix(warp_matrix); vxReleaseScalar(strength_thresh); vxReleaseScalar(num_corners); vxReleaseScalar(shift); vxReleaseArray(corners); return graph; } vx_reference getGraphParameter(vx_graph graph, vx_uint32 index) { vx_parameter p = vxGetGraphParameterByIndex(graph, index); vx_reference ref = NULL; vxQueryParameter(p, VX_PARAMETER_REF, ref, sizeof(ref)); vxReleaseParameter(p); return ref; } void showResults(vx_graph graph, vx_image image, const char * message) { vx_context context = vxGetContext((vx_reference)graph); puts(message); vxSetGraphParameterByIndex(graph, 0, (vx_reference)image); if (VX_SUCCESS == vxProcessGraph(graph)) { vx_size num_corners_value = 0; vx_keypoint_t *kp = calloc( 100, sizeof(vx_keypoint_t)); errorCheck(context, vxCopyScalar((vx_scalar)getGraphParameter(graph, 2), num_corners_value, VX_READ_ONLY, VX_MEMORY_TYPE_HOST), vxCopyScalar failed); printf(Found %zu corners with non-max suppressionn, num_corners_value); /* Array can only hold 100 values */ if (num_corners_value 100)
  • 46. Build your first OpenVX program 29 num_corners_value = 100; errorCheck(context, vxCopyArrayRange((vx_array)getGraphParameter(graph, 1), 0, num_corners_value, sizeof(vx_keypoint_t), kp, VX_READ_ONLY, VX_MEMORY_TYPE_HOST), vxCopyArrayRange failed); for (int i=0; inum_corners_value; ++i) { printf(Entry %3d: x = %d, y = %dn, i, kp[i].x, kp[i].y); } free(kp); } else { printf(Graph processing failed!); } } int main(void) { vx_context context = vxCreateContext(); errorCheck(context, vxGetStatus((vx_reference)context), Could not create a vx_contextn); vx_graph graph = makeTestGraph(context); vx_image image1 = makeInputImage(context, 30, 10); vx_image image2 = makeInputImage(context, 25, 25); showResults(graph, image1, Results for Image 1); showResults(graph, image2, Results for Image 2); vxReleaseContext(context); return 0; }
  • 47. 30 OpenVX Programming Guide 2.4 Image input and output In the example code, a simple library is included, which provides input and output of portable pixelmap (.ppm) and portable greyscale map (.pgm) files. This may be found in the files ppm-io/readImage.h, ppm-io/readImage.c, ppm-io/writeImage.h, and ppm-io/writeImage.c, for you to statically link with the examples. You can see these in action; example4/example4a.c is just the same as example4/example4.c except that it adds another output parameter to the graph, the image that has its corners counted, and writes this to a .pgm file so that you can see the image created. The main() function looks like this: int main(void) { vx_context context = vxCreateContext(); errorCheck(context, vxGetStatus((vx_reference)context), Could not create a vx_contextn); vx_graph graph = makeTestGraph(context); vx_image image1 = makeInputImage(context, 30, 10); vx_image image2 = makeInputImage(context, 25, 25); showResults(graph, image1, Results for Image 1); writeImage((vx_image)getGraphParameter(graph, 3), example4-1.pgm); showResults(graph, image2, Results for Image 2); writeImage((vx_image)getGraphParameter(graph, 3), example4-2.pgm); vxReleaseContext(context); return 0; } 2.4.1 Converting to and from portable pixmap format Portable pixmaps may be viewed under Linux using standard image viewers and on Windows using “Irfanview”. There are a variety of tools that may be used to convert other formats to portable pixmap format, for example, on Linux, you can convert a portable pixmap to jpeg simply by $ convert image.ppm image.jpg Similarly, a conversion the other way is just as easy: $ convert image.jpg image.ppm
  • 48. Build your first OpenVX program 31 The “convert” command is part of the “imagemagick” package and sup- ports very many formats. If it is not on your system, then try the following command: $ sudo apt-get install imagemagick On Windows, Irfanview can read and write a wide variety of image for- mats, and after opening in one format, an image may be saved in another format. This conversion may also be done on the command line using the “/convert” option. For more information, see www.irfanview.com. 2.4.2 Other libraries for input and output of images There are many other libraries useful for reading and writing images, per- haps most notably OpenCV. Other useful and less heavyweight libraries are libpng (see http://www.libpng.org/pub/png/libpng.html), libjpeg (see http://libjpeg.sourceforge.net/), and of course imagemagick; see http:// www.imagemagick.org. 2.4.3 Image processing exercises Another example, “example4/changeImage.c”, is a framework that reads in an RGB (.ppm) image and processes it before outputting it again. It uses a Canny edge detector to find edges in the intensity and adds these back in to the image as black outlines. The graph is constructed in the function “makeTestGraph”, and the processing follows the flow shown in Fig. 2.3. Here we are using the Canny Edge Detect filter to find edges in an image. This is a more complex and more sophisticated algorithm than the method we used to detect edges in the previous examples (i.e., the Sobel filter followed by magnitude calculation) and is more suitable for use with greyscale images. A description of the Canny detector (and many other things!) may be found on the University of Edinburgh School of Informat- ics homepages [34]. The image we are operating on is the Y (luma) channel of an image that has been converted from RGB. We recombine the Y, U, and V planes back into a single image before converting it back to RGB to output again. An example of the results are shown in Fig. 2.4. We leave it for the reader to experiment: 1. What are the effects of changing the hysteresis values in the Canny edge detector? 2. Why use the intensity for finding edges—what about changes in color?
  • 49. 32 OpenVX Programming Guide Figure 2.3 Visualization of the graph in the “changeImage” example. Graph nodes rep- resent functions, and the directed links define input and output data. 3. I want thicker lines in my outlines. 4. I want to recolor this using just five colors. 5. I want a color-free “halo” around the lines. 6. I want to run this graph on several different images. Figure 2.4 Example of input and output images for the “changeImage” example. An ex- ample image (left) and corresponding output with edges added (right). 2.4.4 Answers, or at least hints, for the exercises 1. Setting the upper tracking threshold too low increases the number of undesirable edge fragments appearing in the output; setting the lower tracking threshold too high will cause noisy edges to break up, so set
  • 50. Build your first OpenVX program 33 upper_value quite high and lower_value quite low; the original values are not the best! 2. Try using the other extracted channels (U, V, or both) to find edges and then place them on the Y plane as black lines. 3. Dilate the lines before adding them. 4. Use a look-up table or two convert depth operations back-to-back to reduce the number of colors. 5. Create two sets of lines, one dilated more than the other. The wider ones can be used to remove color and increase intensity on the Y plane before ANDing the thinner ones as black lines. 6. Add parameters to the graph—you will need no any Copy nodes as the input image goes only to one node.
  • 51. CHAPTER 3 Building an OpenVX graph Contents 3.1. Linking nodes 36 3.2. Virtual images 37 3.3. Graph verification and execution 39 3.4. Parameter validation 43 3.5. What can be changed at runtime? 44 3.6. Example 46 To build an OpenVX graph, we start by creating all the data objects we will use in the graph. Once all the data objects we need are created, we can create the graph object itself and all the nodes that will do the actual computations using our data objects as parameters to the node-creation functions. As we create the nodes, we insert them into the graph and link together via the node parameters. Finally, we validate and execute the graph. Once the graph has been built and validated, we can execute it many times with different input data. In this chapter, we illustrate this with a simple background subtrac- tion example. Suppose we want to identify all the “foreground” pixels in a scene. We have an image of the “background” (we will talk about how to create such a background image in a later chapter) and a current im- age of the same scene that might have some foreground objects in it. For our purposes, foreground just means “not background.” One way to get an idea of where the foreground objects are located is to simply subtract the background image from the current image, take the absolute value of all the pixel differences, and apply some threshold to these differences. In other words, if a pixel in the current image is sufficiently different from the background, we call it a foreground pixel. Now there are a number of problems with this simple approach, for example, a small camera movement or lighting change can make everything become “foreground,” but we will ignore that for the purposes of this simple example. Let us see how this looks in OpenVX. We need image data objects for the background image, current image, difference image, and final (binary) foreground image. The code at the top of the next page creates these. OpenVX Programming Guide https://doi.org/10.1016/B978-0-12-816425-9.00009-7 Copyright © 2020 Elsevier Inc. All rights reserved. 35
  • 52. 36 OpenVX Programming Guide vx_image bg_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8); vx_image curr_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8); vx_image diff_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8); vx_image fg_image = vxCreateImage(ctxt, 640, 480, VX_DF_IMAGE_U8); In a real production code, we should check that all the data objects were created successfully, but we leave this out of this sample code for simplicity. In this example, we also need to create a threshold object and set it to some initial value: vx_threshold threshold = vxCreateThresholdForImage(ctxt, VX_THRESHOLD_TYPE_BINARY, VX_DF_IMAGE_U8, VX_DF_IMAGE_U8); vx_status status; vx_uint8 threshval = 10; status = vxCopyThresholdValue(threshold, threshval, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST); In this example, we use 10 as the threshold, so if the grayscale value of the current image is different from the background by more than 10, then we call it a foreground pixel. We use the default values for the threshold outputs, so the foreground pixels will be set to 255 (0xFF), and the rest will be set to zero. Now that we have the necessary data objects, we can build a simple graph and link it up. 3.1 Linking nodes In OpenVX, nodes are linked via the parameters to the node creation func- tions. If an output parameter of one node is the same OpenVX data object as the input parameter of another node, then the nodes are linked via this data object. There is no separate explicit “link” call. This enables OpenVX code to look very similar to traditional non-graph code. For our back- ground subtraction example, we just have two nodes, an absolute difference (or absdiff) node and a threshold node (thresh). Here is the code to create the graph and insert the nodes: vx_graph g = vxCreateGraph(ctxt); vx_node absdiff = vxAbsDiffNode(g, bg_image, curr_image, diff_image); vx_node thresh = vxThresholdNode(g, diff_image, threshold, fg_image); The first line creates the graph object g within our OpenVX context. The next two lines create the absdiff and threshold nodes, respectively.
  • 53. Other documents randomly have different content
  • 54. were dwindling in numbers, and it was this marriage of a Comte de Foix with the heiress of Béarn which caused practically the extinction of one. The modern department of the Ariège, of which the ancient Comté de Foix formed the chief part, possesses few historical monuments dating before the Middle Ages. There are numerous residential châteaux scattered about, and the most splendid of them all is at Foix itself. Fine old churches and monasteries, and quaint old houses are numerous; yet it is a region less exploited by tourists than any other in France. Not all these historic shrines remain to-day unspoiled and untouched. Many of them were destroyed in the Revolution, but their sites and their ruins remain. The mountain slopes of this region are thickly strewn with watch-towers and observatories; and though all but fallen to the ground they form a series of connecting historical links which only have to be recognized to be read. The towers or châteaux of Quié, Tarascon-sur- Ariège, Gudanne, Lourdat and Vic-Dessos are almost unknown to most travellers. They deserve to become better known, however, especially Lourdat, one of the most spectacularly endowed château ruins extant. The fourteenth century was the most brilliant in the history of Foix. These were the days of Gaston Phœbus; and the description of his reception of Charles VI of France at Mazères, as given by the chroniclers, indicates an incomparable splendour and magnificence. Gaston Phœbus, like Henri de Béarn, was what might be called a good liver. Here is how he spent his day—when he was not warring or building castles. He rose at noon and after a mass he dined. Usually there were a great number of dishes; and, on really great occasions, as on a fête or festin, the incredible number of two hundred and fifty. These princes of the Pyrenees loved good cheer, and their usage was to surcharge the tables and themselves with the good things until the results were uncomfortable. Gaston’s two sons, Yvain and Gratain, usually stood behind him at table, and the youngest son, another Gaston, first tried all the dishes before his august father ate of them. He was weak and sickly, a “mild and melancholy figure,” and no wonder! The feasting terminated, Gaston and his court would pass into the Salle de Parlement, “where many things were debated,” as the chroniclers put it. Soon entered the minstrels and troubadours, while in the courts there were trials of skill between the nobles of one house and another, stone throwing, throwing the spear, and the jeu de paume. The count—“toujours magnifique” (no
  • 55. chronicler of the time neglects to mention that fact)—distributed rewards to the victors. After this there was more eating, or at least more drinking. When he was not sleeping or eating or amusing himself, or conducting such affairs as he could not well depute to another, such as the planning and building of castles, Gaston occupied himself, like many other princes of his time, with belles-lettres and poesy. He had four secrétaires to do his writing; and it is possible that they may have written much which is attributed to him, if the art of employing literary “ghosts” was known in that day. He composed chansons, ballades, rondeaux and virelais, and insisted on reading them aloud himself, forbidding any one to make a comment on them. How many another author would like to have the same prerogative! Gaston Phœbus de Foix, so named because of his classic beauty, was undoubtedly a great author in his day. This bold warrior wrote a book on the manners and usage of hunting in mediæval times, entitled the “Miroir de Phœbus;” and, while it might not pass muster among the masterpieces of later French literature, it was a notable work for its time and literally a mirror of contemporary men and manners in the hunting field. Gaston de Foix was another gallant noble. He died at the age of twenty- four at the Battle of Ravenna in 1512. Jacques Fournier, who became Pope Benoit XII, also came from Foix. The honour of being the most celebrated of the Counts of Foix may well be divided by Gaston Phœbus (1343-1390) and Henri Quatre (1553-1610). The latter was the last of the famous counts of the province; and he it was who united it with the royal domain of France, thus sinking its identity for ever, though his predecessors had done their utmost to keep its independence alive. During the Hundred Years War the Comtes de Foix, masters of the entire middle chain of the Pyrenees, were the strongest power in the southwest; and above all were they powerful because of their alliances and relations with the Spanish princes, whose friendship and aid were greatly to be desired, for their support meant success for their allies. This is proven, absolutely, from the fact that, when the English were ultimately driven from France, it was through the aid and support of Gaston Phœbus himself and his successors, Archambaud, Jean I and Gaston IV.
  • 56. The fifteenth century saw the apogee of the house of Foix. One of its princes married Madeleine de France, sister of Louis XI. The sixteenth century saw sad times during a long civil war of more than thirty years duration. War among the members of a household or among one’s own people is really an inexcusable thing. In the Comté the Abbey of Boulbonne was destroyed. At Pamiers all the religious edifices were razed; and the Abbey of St. Volusien at Foix, the special pride of the counts for ages, was destroyed by fire. Calm came for a period under the reign of Henri IV, at Paris; but, after his death, local troubles and dissensions broke out again, inspired and instigated by the wily Duc de Rohan, which culminated at Pamiers, where the great Condé and Montmorenci appeared at the head of their troops. The peace of Alais ended this final struggle; and, to assure the security of the country, Richelieu gave the order to dismantle all the walls and ramparts of the fortified places in the Comté, and all the châteaux-forts as well. This was done forthwith, and that is why many a mediæval château in these parts is in ruins to-day. The Château de Foix, by reason of its dignity, was allowed to keep its towers and battlemented walls. For a hundred and fifty years, that is up to the Revolution, Foix was comparatively tranquil. Under the reign of Louis XIV, however, the region saw the frequent passage of troops and warlike stores as they came and went to the Spanish wars. This nearly ruined many dwellers in town and country by reason of the tax they had to pay in money and provisions. Like the Basques and the Béarnais the inhabitants of the Ariège, the descendants of the old adherents of the Comtes de Foix, bear many traces of their former independence and liberty. Civilization and their easy, comfortable manner of living have not made of them a very robust race, but they are possessed of much fairness of face and figure and gentleness of manner. The smugglers of feudal times, and considerably later times for that matter, were the pest of the region. It was rude, hard work smuggling wines or tobacco over the mountains, in and out of Spain, and its wages were uncertain, but there were large numbers who embarked on it in preference to grazing flocks and herds or engaging in other agricultural pursuits. It was hard work for the smugglers of Foix to get their burdens up the mountains, but they had a custom of rolling their load up into great balls
  • 57. bound around with wool and thongs and rolling them down the other side. Thus the labour was halved. The Romany chiel or gypsy adopted the contraband business readily; and with the competition of the French and Spanish, there were lively times on the frontier between Foix and Gascogne and Spain and Andorra. M. Thiers recounts an adventure in an auberge of the Pyrenees with such a crew of bandits, and thought himself lucky to escape with his life. The chief of the band, as the travellers were all sitting around the great log fire, began cleaning his pipe with a long poignard-like knife which, he volunteered, was ready to do other service than whittling bread or tobacco if need be. The night passed off safely enough by reason of the arrival of a squad of gendarmes, but the next night a whole house full of travellers were murdered on the same spot. The roads of the old Comté de Foix, a very important thing for many who travel by automobile, are throughout excellent and extensive. There are fourteen Routes Nationales and Départementales crossing in every direction. The highway from Toulouse to Madrid runs via St. Girons and Bayonne into Andorra by way of the valley of the Ariège, and to Barcelona via Perpignan and the Col de Perthus. The valley of the Ariège, to a large extent included in the Comté de Foix, has a better preserved historical record than its neighbours on the east and west. In the ninth century the ruling comte was allied with the houses of Barcelona and Carcassonne. His residence was at Foix from this time up to the Revolution; and his rule embraced the valley of the Hers, of which Mirepoix was the principal place, the mountain region taken from Catalogne, and a part of the lowlands which had been under the scrutiny of the Comtes de Toulouse.
  • 58. CHAPTER XI FOIX AND ITS CHÂTEAU FOIX, of all the Préfectures of France of to-day, is the least cosmopolitan. Privas, Mende and Digne are poor, dead, dignified relics of the past; but Foix is the dullest of all, although it is a very gem of a smiling, diffident little wisp of a city, green and flowery and astonishingly picturesque. It has character, whatever it may lack in progressiveness, and the brilliant colouring is a part of all the cities of the South. Above the swift flowing Ariège in their superb setting of mountain and forest are the towers and parapets of the old château, in itself enough to make the name and fame of any city. Architecturally the remains of the Château de Foix do not, perhaps, rank very high, though they are undeniably imposing; and it will take a review of Froissart, and the other old chroniclers of the life and times of the magnificent Gaston Phœbus, to revive it in all its glory. A great state residence something more than a mere feudal château, it does not at all partake of the aspect of a château-fort. It was this last fact that caused the Comtes de Foix, when, by marriage, they had also become seigneurs of Béarn, to abandon it for Mazères, or their establishments at Pau or Orthez. Foix nevertheless remained a proud capital, first independent, then as part of the province of Navarre, then as a province of the Royaume de France; and, finally, as the Préfecture of the Département of Ariège. The population in later times has grown steadily, but never has the city approached the bishopric of Pamiers, just to the northward, in importance. Many towns in this region have a decreasing population. The great cities like Toulouse and Bordeaux draw upon the youth of the country for domestic employment; and, lately, as chauffeurs and manicurists, and in comparison to these inducements their native towns can offer very little. If one is to believe the tradition of antiquity the “Rocher de Foix,” the tiny rock plateau upon which the château sits, served as an outpost when the Phoceans built the primitive château upon the same site. Says a Renaissance
  • 59. historian: “On the peak of one of nature’s wonders, on a rock, steep and inaccessible on all sides, was situated one of the most ancient fortresses of our land.” In Roman times the site still held its own as one of importance and impregnability. A representation of the château as it then was is to be seen on certain coins of the period. This establishes its existence as previous to the coming of the Visigoths in the beginning of the sixth century. The first written records of the Château de Foix date from the chronicles of 1002, when Roger-le-Vieux, Comte de Carcassonne, left to his heir, Bernard- Roger, “La Terre et le Château de Foix.” The Château de Foix owes its reputation to its astonishingly theatrical site as much as to the historic memories which it evokes, though it is with good right that it claims a legendary renown among the feudal monuments of the Pyrenees. All roads leading to Foix give a long vista of its towered and crenelated château sitting proudly on its own little monticule of rock beside the Ariège. Its history begins with that of the first Comtes de Foix, the first charter making mention thereof being the last will and testament of Roger-Bernard, the first count, who died in 1002. During the wars against the Albigeois the château was attacked by Simon de Montfort three times, in 1210, 1212, 1213, but always in vain. Though the surrounding faubourgs were pillaged and burned the château itself did not succumb. It did not even take fire, for its rocky base gave no hold to the flames which burned so fiercely around it. The most important event of the château’s history happened in 1272 when the Comte Roger-Bernard III rebelled against the authority of the Seneschal-Royal of Toulouse. To punish so rebellious a vassal, Philippe-le- Hardi came forthwith to Foix at the head of an army, and himself undertook the siege of the château. At the end of three days the count succumbed, with the saying on his lips that it was useless to cut great stones and build them up into fortresses only to have them razed by the first besiegers that came along. Whatever the qualifications of the third Roger-Bernard were, consistent perseverance was not one of them. Just previous to 1215, after a series of intrigues with the church authorities, the château became a dependence of the Pope of Rome; but at a council of the Lateran the Comte Raymond-Roger demanded the justice
  • 60. that was his, and the new Pope Honorius III made over the edifice to its rightful proprietor. During the wars of religion the château was the storm-centre of great military operations, of which the town itself became the unwilling victim. In 1561 the Huguenots became masters of the city. Under Louis XIII it was proposed to raze the château, as was being done with others in the Midi, but the intervening appeal of the governor saved its romantic walls to posterity. In the reign of Louis XIV the towers of the château were used as archives, a prison and a military barracks, and since the Revolution—for a part of the time at least—it has served as a house of detention. When the tragic events of the Reformation set all the Midi ablaze, and Richelieu and his followers demolished most of the châteaux and fortresses of the region, Foix was exempted by special orders of the Cardinal-Minister himself. Another war cloud sprang up on the horizon in 1814, by reason of the fear of a Spanish invasion; and it was not a bogey either, for in 1811 and 1812 the Spaniards had already penetrated, by a quickly planned raid, into the high valley of the Ariège. In 1825 civil administration robbed this fine old example of mediæval architecture of many of those features usually exploited by antiquarians. To increase its capacity for sheltering criminal prisoners, barracks and additions—mere shacks many of them—were built; and the original outlines were lost in a maze of meaningless roof-tops. Finally, a quarter of a century later, the rubbish was cleared away; and, before the end of the century, restoration of the true and faithful kind had made of this noble mediæval monument a vivid reminder of its past feudal glory quite in keeping with its history.
  • 61. Ground Plan of the Château de Foix The actual age of the monument covers many epochs. The two square towers and the main edifice, as seen to-day, are anterior to the thirteenth century, as is proved by the design in the seals of the Comtes de Foix of 1215 and 1241 now in the Bibliothèque Nationale in Paris. In the fourteenth century these towers were strengthened and enlarged with the idea of making them more effective for defence and habitation. CHÂTEAU DE FOIX The escutcheons of Foix, Béarn and Comminges, to be seen in the great central tower, indicate that it, too, goes back at least to the end of the fourteenth century, when Eleanore de Comminges, the mother of Gaston Phœbus, ruled the Comté.
  • 62. Key of the Vaulting, Château de Foix. Showing the Arms of the Comtes de Foix The donjon or Tour Ronde arises on the west to a height of forty-two metres; and will be remarked by all familiar with these sermons in stone scattered all over France as one of the most graceful. Legend attributes it to Gaston Phœbus; but all authorities do not agree as to this. The window and door openings, the mouldings, the accolade over the entrance doorway and the machicoulis all denote that they belong to the latter half of the fifteenth century. These, however, may be later interpolations. Originally one entered the château from exactly the opposite side from that used to-day. The slope leading up to the rock and swinging around in front of the town is an addition of recent years. Formerly the plateau was gained by a rugged path which finally entered the precincts of the fortress through a rectangular barbican. Finally, to sum it up, the pleasant, smiling, trim little city of Foix, and its château rising romantically above it, form a delightful prospect. Well preserved, well protected, and for ever free from further desecration, the Château de Foix is as nobly impressive and glorious a monument of the Middle Ages as may be found in France, as well as chief record of the gallant days of the Comtes de Foix.
  • 63. Foix’ Palais de Justice, built back to back with the rock foundations of the château, is itself a singular piece of architecture containing a small collection of local antiquities. This old Maison des Gouverneurs, now the Palais de Justice, is a banal, unlovely thing, regardless of its high-sounding titles. In the Bibliothèque, in the Hôtel de Ville, there are eight manuscripts in folio, dating from the fifteenth century, and coming from the Cathedral of Mirepoix. They are exquisitely illuminated with miniatures and initials after the manner of the best work of the time. It was that great hunter and warrior, Gaston Phœbus who gave the Château de Foix its greatest lustre. It was here that this most brilliant and most celebrated of the counts passed his youth; and it was from here that he set out on his famous expedition to aid his brother knights of the Teutonic Order in Prussia. At Gaston’s orders the Comte d’Armagnac was imprisoned here, to be released after the payment of a heavy ransom. As to the motive for this particular act authorities differ as to whether it was the fortunes of war or mere brigandage. They lived high, the nobles of the old days, and Froissart recounts a banquet at which he had assisted at Foix, in the sixteenth century, as follows:— “And this was what I saw in the Comté de Foix: The Count left his chamber to sup at midnight, the way to the great salle being led by twelve varlets, bearing twelve illumined torches. The great hall was crowded with knights and equerries, and those who would supped, saying nothing meanwhile. Mostly game seemed to be the favourite viand, and the legs and wings only of fowl were eaten. Music and chants were the invariable accompaniment, and the company remained at table until after two in the morning. Little or nothing was drunk.” Froissart’s description of the table is simple enough, but he develops into melodrama when he describes how the count killed his own son on the same night—a tragic ending indeed to a brilliant banquet. “‘Ha! traitor,’ the Comte said in the patois, as he entered his sleeping son’s chamber; ‘why do you not sup with us? He is surely a traitor who will not join at table.’ And with a swift, but gentle drawing of his coutel (knife) across his successor’s throat he calmly went back to supper.” Truly, there were high doings when
  • 64. knights were bold and barons held their sway. They could combat successfully everything but treachery; but the mere suspicion of that prompted them to take time by the forelock and become traitors themselves. Foix has a fête on the eighth and ninth of September each year, which is the delight of all the people of the country round about. Its chief centre is the Allées de Vilote, a great tree-shaded promenade at the base of the château. It is brilliantly lively in the daytime, and fairy-like at night, with its trees all hung with great globes of light. A grand ball is the chief event, and the “Quadrille Officiel” is opened with the maire and the préfet at the head. After this comes la fête générale, when the happy southrons know no limit to their gaieties. There are three great shaded promenades, and in each is a ball with its attendant music. It is a pandemonium; and one has to be habituated to distinguish the notes of one blaring band from the others. The central park is reserved for the country folk, that on the left for the town folk, and that on the right for the nobility. This, at any rate, was the disposition in times past, and some sort of distinction is still made. In suburban Foix, out on the road to Pamiers, is the little village of St. Jean-de-Vergues. It has a history, of course, but not much else. It is a mere spot on the map, a mere cluster of houses on the Grande Route and nothing more. In the days of the Comte Roger-Bernard, however, when he would treat with the king of France, and showed his willingness to become a vassal, its inhabitants held out beyond all others for an “indépendance comtale.” They didn’t get it, to be sure, but with the arrival of Henri Quatre on the throne of France, the vassalage became more friendly than enforced.
  • 65. CHAPTER XII THE VALLEY OF THE ARIÈGE THE entire valley of the Ariège, from the Val d’Andorre until it empties into the Garonne at Toulouse, contains as many historic and romantic reminders as that of any river of the same length in France. Saverdun and Mazères, between Toulouse and Pamiers, and perhaps fifty kilometres north of Foix, must be omitted from no historical trip in these parts. Saverdun sits close beside one of the few remaining columns which formerly marked the boundary between Languedoc and Gascogne, a veritable historical guide-post. It was one of the former fortified towns of the Comté de Foix. It is an unimportant and unattractive enough place to- day, if a little country town of France can ever be called unattractive, but it is the head centre of innumerable châteaux and country houses of other days hidden away on the banks of the Ariège. Mostly they are without a traceable history, but everything points to the fact that they played an important part in the golden days of chivalry, and such names as l’Avocat- Vieux, Frayras, Larlenque, Madron, Pauliac and Le Vigne—the oldtime manor of the family of Mauvasin—will suggest much to any who know well their mediæval history. A diligence runs to-day from Saverdun to Mazères, the birthplace of the gorgeous and gallant Gaston of Foix, the hero of Ravenna. Mazères is a most ancient little town, built on the banks of a small river, the Hers, and in the thirteenth century was surrounded by important fortifications, now mostly gone to build up modern garden walls. Around the old ramparts has been laid out a series of encircling boulevards, which, as an expression of civic improvement, is far and away ahead of the squares and circles of new western towns in America. The encircling boulevard is one, if not the chief, charm of very many French towns. The ruins of the ancient château where was born the celebrated Gaston are still seen, but nothing habitable is left to suggest the luxury amid which the youth was brought up. Near by are the châteaux of Nogarède and
  • 66. Nassaure, each of them reminiscent of family names writ large in the history of Foix. Another dozen kilometres southward towards Foix is Pamiers. It is extremely probable that provincial France has changed its manners considerably since the Revolution, but one can hardly believe of Pamiers, to-day a delightful little valley town, all green and red and brown, that a traveller with a jaundiced eye once called it “an ugly, stinking, ill-built hole with an inn—of sorts,” This is not the aspect of the city, nor does it describe the Hôtel Catala. Pamiers owes its origin to the erection of a feudal château by Comte Roger II on his return from the Holy Land, and which he called Apamea or Apamia, in memory of his visit to Apamée in Syria. Evolution has readily transformed the name into Pamiers. Virtually, so far as its lands went, the place belonged to a neighbouring abbey, but as the monks were forced to call upon the Comtes de Foix to aid them in protecting their property from the Comtes de Carcassonne, the title rights soon passed to the ruling house of Foix. In 1628 Condé pillaged and sacked the city, and not a vestige now remains of its once proud château, save such portions as may have been built into and hidden in other structures. The site of the old château is preserved in the memory only by the name of Castellat, which has been given to a singularly beautiful little park and promenade. It was in the thirteenth century that a Bishop of Pamiers, the legate of Pope Boniface VIII, insulted Philippe-le-Bel in full audience of his parlément. The king, resentful, drove him from the council, and a Bull of Pope Boniface delivered the bishop to an ecclesiastical tribunal. So far, so good, but Boniface issued another Bull demanding that the king of France submit to papal power in matters temporal as well as in matters spiritual. Thus a pretty quarrel ensued, beginning with the famous letter from the king, which opened thus: “Philippe, by the grace of God, King of the French, to Boniface, the pretended Pope, has little or no reason for homage....” Pamiers itself is a dull little provincial cathedral town, lying low in a circle of surrounding hills. Its churches are historically famous, and architecturally varied and beautiful, and the octagonal belfry of its cathedral (1512), in the style known as “Gothic-Toulousain,” is particularly admirable.
  • 67. Mirepoix, a dozen kilometres east of Pamiers, is interesting. The Seigneurie of Mirepoix became an appanage of Guy de Levis, maréchal in the army of Simon de Montfort in the thirteenth century, but the legislators of Revolutionary times, disregarding the usage of five centuries, coupled the control of the affairs of the region with those of Foix, from which it had indeed been separated long ages before. Mirepoix has, nevertheless, an individuality and a history quite its own. In 1317 it was made a bishopric, and was under the immediate control of the Seneschalship of Carcassonne. It had, by parent right, a certain attachment for Foix, but by the popular consent of its people none at all; thus it lay practically under the sheltering wing of Languedoc. The descendants of Guy de Levis were distinguished in the army, in diplomacy and held many public offices of trust at Paris. Under Louis XV the last representative of the family was made a “Duc, Maréchal de France et Gouverneur de Languedoc.” It was his cousin, François de Levis-Ajac (from whom Levis opposite Quebec got its name), who became also Maréchal de France, and illustrious by reason of his defence of Canada. The Château de Montségur, in the valley of the Hers, was the scene of the last stand of the Albigeois tracked to their death by the inquisitors. Just westward of Foix is La Bastide-de-Serou, founded in 1254, another of those ancient bastides with which this part of the Midi was covered in mediæval times. To-day it is a mere nothing on the map, and not much more in reality, a dull, sad town, whose only liveliness comes from the exploitations of a company whose business it is to dig phosphate and bauxite from the hillsides round about. Below La Bastide is the Château de Bourdette, charmingly set about with vines in a genuine pastoral fashion. For a neighbour, not far away, there is also the Château de Rodes, set in the midst of a forest of mountain ash and quite isolated. Either, if they are ever put on the market (for they are inhabitable to-day), would make a good retiring spot for one who wanted to escape the strenuous cares and hurly-burly of city life. South of Foix is Tarascon-sur-Ariège, a name which has a familiar sound to lovers of fiction and readers of Daudet. It was not at Tarascon-sur-Ariège where lived Daudet’s estimable bachelor, Tartarin, but Tarascon-sur-Rhône in Provence. Daudet pulled the latter smug little town from obscurity and oblivion—even though the inhabitants said that he had slandered them—but
  • 68. nothing has happened that gives distinction to the Tarascon of the Pyrenees since the days when its seigneurs inhabited its château.
  • 69. Tarascon-sur-Ariège Reminders of the town’s mediæval importance are few indeed, and of its château only a lone round tower remains. There are two fortified gateways in the town still above ground, and two thirteenth-century church towers which take rank as admirable mediæval monuments. Tarascon was one of the four principal fortified towns of the Comté de Foix, but suffered by fire, and for ever since has languished and dozed its days away, so that not even a passing automobile will wake its dwellers from their somnolence. Tarascon has a fine and picturesque bridge over the Ariège which intrudes itself in the foreground from almost every view- point. It is not old, however, but the work of the last century. Here nearly everything is of the mouldy past and rusty with age and tradition, though there is a local iron industry something considerable in
  • 70. extent. The highroad from Foix into Andorra cuts the town directly in halves, and on either side are narrow, climbing streets running up the hillside from the river bank, but architectural or topographical changes have been few since the olden times. Tarascon’s population—though the place is the market town of the commune—has, in a hundred years, fallen from fifteen hundred to fourteen hundred and forty five, to give exact statistical figures, which are supposed not to lie. Such observations in France really prove nothing, not even that signs of progress are wanting, nor that folk are less prosperous; they simply suggest that its cities and towns are self-satisfied and content, and are not ambitious to outdistance their neighbours in alleged civic improvements of doubtful taste—always at the tax-payers’ expense. Tarascon of itself might well be omitted from a Pyrenean itinerary, but when one includes the neighbouring church of Notre Dame de Sabart—a place of pilgrimage for the faithful of the whole region of the Pyrenees on the eighth and fifteenth of September—the case were different. It is one of the sights and shrines of the region, as is that of Stes. Maries-de-la-Mer in Provence, or Notre Dame de Laghat in the old Comté de Nice. The old abbey-fortress built here by Charlemagne has disappeared, but the great Romanesque church, with its three great naves, is avowedly built up from the remains of the former edifice. Most of Charlemagne’s handiwork has vanished throughout his kingdom, but the foundations remain, here and there, and upon them has been built all that is best and most enduring in Gaul. In the environs it was planned to make a great centre of affairs, but destiny and the Comtes de Foix ruled otherwise, though, curiously enough, up to the Revolution the “Prétres de Sabart” ruled with an iron-bound supremacy many of the affairs of neighbouring parishes which were no business of theirs. It was church and state again in conflict, but the Revolution finished that for the time being. Like many of the pardons of Brittany, or the fête of Les Saintes Maries in Provence, the fête of Notre Dame de Sabart commences as a religious function, but degenerates finally into a Fête Profane, with dancing, bull- baiting, and eating and drinking to the full. It is perhaps not a wholly immoral aspect that the fête takes on; certainly the participants do not act in
  • 71. any manner outrageous; but by contrast the thing is bound to be remarked by westerners, and probably misjudged and set down as something worse than it is. Bull-baiting, for instance, sounds bad, but when one learns that it consists only of trying to snatch a ribbon rosette from between the bull’s horns—for a prize of three francs for a blue one, and five francs for a red one, the bull carrying the red rosette being, supposedly, more vicious and savage than the others—the whole thing resolves itself into a simple, harmless amusement, far more dangerous for the amateur rosette picker than the bull, who really seems to enjoy it. Vic Dessos, just southwest of Tarascon, is a quaint little mountain town, with the ruins of the Château de Montréal and a twelfth-century church as attractions for the traveller. The savage surroundings of Vic, the denuded mountain peaks, and the deep valleys, bring tempests and thunderstorms in their train with astonishing violence and frequency. The clouds roll down like a pall, suddenly, at any time of the year, and as quickly pass away again. The phenomena have been remarked by many travellers in times past, and one need not fear missing it if he stays anything over three hours within a fifty-kilometre radius. If this offers anything of a sensation to one, Vic Dessos should be visited. You can arrive by diligence from Tarascon, and can get comfortably in out of the rain at the excellent Hôtel Benazet. From Tarascon to Ax-les-Thermes, still in the valley of the Ariège, is twenty-five kilometres of superb roadway. All the way are strung out groups of dainty villages surrounded with cultivated country. Here and there is an isolated mass of rock, a round watch-tower, or a ruined fortress, still possessing its crenelated walls to give an attitude of picturesqueness. There are innumerable little villages, a whole battery of them, linked together. At the end of this long peopled highway is an unpretentious mediæval country house, of that class known as a gentilhommière, of fawn-coloured stone, and still possessing its two flanking sentinel towers preserved in all the romantic grimness of their youth. At the junction of the Ariège with the Ascou, the Oriège, the Lauze and the Foins is Ax-les-Thermes—the ancient Aquæ of the Romans, and now a “thermal station” of the first rank. Primarily Ax is noted for its sulphurous waters, but for the lover of romantic days and ways its architectural and Historical monuments are of the first consideration. The ruins of the Château des Maures, the ancient Castel Maü, are the chief of these
  • 72. monuments, while a neighbouring peak of rock bears aloft an enormous square tower surmounted by a statue of the Virgin. There are sixty-one “sources” at Ax-les-Thermes giving a supply of medicinal waters. In part they were known to the Romans, and in 1260 Saint Louis founded a hospital here for sick soldiers returning from the Crusades. Ax-les-Thermes is not a howlingly popular watering place, but it is far more delightful than Luchon, Cauterets or Bigorre, if quaintness of architecture, manners and customs, and modesty of hotel prices count for anything. The Porte et Pont d’Espagne at Ax is one of the most interesting architectural reminders of the past that one will find throughout the Pyrenees. The bridge itself is but a diminutive span carrying a narrow roadway, which if not forbidden to automobile traffic should be, for the negotiating of this bridge and road, and the low, arched gateway at the end, will come very near to spelling disaster for any who undertakes it. Throughout the neighbourhood one sees more than an occasional yawning pit’s mouth. All through the Comté de Foix were exploited, and are yet to some extent, iron mines and forges, the latter known as Forges Catalans. Roger-Bernard, Comte de Foix, in 1293 gave the first charter to the mine-promotors of the neighbourhood, and the industry flourished in many parts of the Comté until within a few generations, when, apparently, the supply of mineral was becoming exhausted. At Luzenac, on the line between Tarascon and Ax, one turns off the road and in a couple of hours, if he is a good brisk walker, makes the excursion to the château-à-pic of Lourdat. There is a little village of the same name at the base of the rocky peak which holds aloft the château, but that doesn’t count. Without question this Château de Lourdat ranks as one of the most spectacular of all the Pyrenean châteaux. Its rank in history, too, is quite in keeping with its extraordinary situation, though nothing very startling ever happened within its walls. It dates from the thirteenth and fifteenth centuries, and outside that of the capital of Foix was the most efficient stronghold the counts possessed. Louis XIII demolished the edifice, in part, fearing its powers of resistance, and as a base from which some new project might be launched against him. Accordingly, it is a ruin to-day, but in spite
  • 73. of this there are still left four pronounced lines of fortifications before one comes to the inner precincts of the château. For this reason alone it ranks as one of the most strongly defended of all contemporary feudal works. Even the old Cité de Carcassonne has but two encircling walls. The square donjon rising in the middle is in the best style of that magnificent royal builder, Gaston Phœbus, and is reminiscent of the works of Foulques Nerra in mid-France. There is also a great ogive-arched portal, or gateway, which made still another defence to be scaled before one finally entered within. In situation and general spectacular effect the Château de Lourdat takes a very near rank to that rock-perched château at Le Puy—“the most picturesque spot in the world.” Château de Lourdat
  • 74. CHAPTER XIII ST. LIZIER AND THE COUSERANS LE Pays de Couserans lies in the valley of the Salat, in the mid-Pyrenees, hemmed in by Foix, Comminges and Spain. Its name is derived from the Euskarans, an Iberian tribe who were here on the spot in the dark ages. The history of the Couserans is not known to anything like the extent of its neighbouring states, and is, accordingly, very little travelled by strangers from afar, save long-bearded antiquarians who come to study St. Lizier, and regret that they were not obliged to come on donkey-back as of old, instead of by rail or automobile. The trouble with antiquarianism, as a profession, or a passion, is that it leads one to fall into a sleepy unprogressiveness which comports little with the modern means at hand for doing things. A photographic plate of a curious Roman inscription is far more truthful and convincing than the most painstaking Ruskinese pencil drawing ever limned, and a good “process-cut” of the broad strokes of some facile modern artist’s brush is more typical of the characteristics of a landscape than the finest wood or steel engraving our grandfathers ever knew. If you like grand mountains, here in Couserans is Mont Vallier, a superb giant of the central chain of the Pyrenees. If it is sweet sloping valleys that you prefer, here they are in all their unspoiled wildness, for the railway actually does stop at St. Girons. If an ice-cold mountain stream would please your fancy, there is the Salat and its tributaries, flowing down by St. Girons and St. Lizier into the Garonne. And, finally, if you wish to roll back the curtain of time you will see in old St. Lizier a stage set with the accessories the reminiscent splendours of which will be scarcely equalled by any other feudal bourg of France. There is no region in the Pyrenees of which less is known historically than the Valley of the Salat. A vicomte reigned here in the sixteenth century, but the seigneury was divided among different branches of the family soon after; and, if they had an archivist among them, he failed to preserve his documents along with the written history of the greater affairs of Toulouse and Foix. Soon religious and civil troubles began to press and much of
  • 75. Welcome to our website – the perfect destination for book lovers and knowledge seekers. We believe that every book holds a new world, offering opportunities for learning, discovery, and personal growth. That’s why we are dedicated to bringing you a diverse collection of books, ranging from classic literature and specialized publications to self-development guides and children's books. More than just a book-buying platform, we strive to be a bridge connecting you with timeless cultural and intellectual values. With an elegant, user-friendly interface and a smart search system, you can quickly find the books that best suit your interests. Additionally, our special promotions and home delivery services help you save time and fully enjoy the joy of reading. Join us on a journey of knowledge exploration, passion nurturing, and personal growth every day! ebookbell.com