Skip to content

Commit 22aa16b

Browse files
authored
Merge pull request #93 from JuliaImages/vs/cleanup-and-coverage
Cleanup (depth table, OpenCVError, split overrides) + coverage 50%→80%
2 parents 0b3abc5 + a6853a3 commit 22aa16b

16 files changed

Lines changed: 344 additions & 168 deletions

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "OpenCV"
22
uuid = "f878e3a2-a245-4720-8660-60795d644f2a"
33
authors = ["Archit Rungta <architrungta120@gmail.com>"]
4-
version = "4.9.1"
4+
version = "4.10.0"
55

66
[deps]
77
CxxWrap = "1f15a43c-97ca-5a2a-ae31-89f07a497df4"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ using OpenCV
3434

3535
Once imported, you can call OpenCV functions and use its data structures. Here's a simple example that loads an image and applies a Gaussian blur:
3636

37-
List of wrapped methods is available here: [wrapped methods](https://github.com/opencv/opencv_contrib/blob/4.x/modules/julia/gen/funclist.csv)
37+
List of wrapped methods is available here: [wrapped methods](https://github.com/JuliaImages/OpenCV.jl/blob/master/gen/funclist.csv)
3838
```julia
3939
using ImageCore, OpenCV, TestImages, MosaicViews, ImageView
4040

codecov.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,10 @@ coverage:
66
patch:
77
default:
88
informational: true
9+
10+
# The CxxWrap bindings under src/generated/ are machine-generated from OpenCV
11+
# headers (see gen/, reproducibility enforced by Regenerate.yml), not
12+
# hand-maintained, so exclude them from coverage. Only the hand-written glue in
13+
# src/ is reported.
14+
ignore:
15+
- "src/generated/**"

src/cv_cxx.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function __init__()
1818
end
1919
const Scalar = Union{Tuple{}, Tuple{Number}, Tuple{Number, Number}, Tuple{Number, Number, Number}, NTuple{4, Number}}
2020

21+
include("errors.jl")
2122
include("Mat.jl")
2223

2324
const InputArray = Union{AbstractArray{T, 3} where {T <: dtypes}, CxxMat}
@@ -55,4 +56,7 @@ julia_to_cpp(c::Char) = Cchar(c)
5556

5657
include("generated/cv_cxx_wrap.jl")
5758

58-
include("cv_manual_wrap.jl")
59+
# Hand-written overrides of the generated wrappers, grouped by concern.
60+
include("overrides/defaults.jl") # default-argument convenience constructors
61+
include("overrides/covariance.jl") # AbstractVector{<:InputArray} covariant overloads
62+
include("overrides/convenience.jl") # GUI callbacks, CascadeClassifier, VideoWriter_fourcc

src/cv_manual_wrap.jl

Lines changed: 0 additions & 129 deletions
This file was deleted.

src/errors.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
OpenCVError(msg) <: Exception
3+
4+
Error raised by OpenCV.jl's binding layer — e.g. an unsupported array element type
5+
when converting to or from a `cv::Mat`.
6+
7+
!!! note
8+
Errors thrown by OpenCV's own C++ code (`cv::Exception`, assertion failures)
9+
currently surface as `ErrorException`s carrying the C++ message. Re-wrapping those
10+
as `OpenCVError` would require the generated wrappers to emit `try`/`catch` and is
11+
not yet done.
12+
"""
13+
struct OpenCVError <: Exception
14+
msg::String
15+
end
16+
17+
Base.showerror(io::IO, e::OpenCVError) = print(io, "OpenCVError: ", e.msg)

src/fileio.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,17 @@ function load(f::File{T}, flags::Int64) where {T<:_IMAGE_DATA_FORMATS}
3232
end
3333

3434
function load(s::Stream{T}) where {T<:_IMAGE_DATA_FORMATS}
35-
data = read(stream(s))
36-
img = imdecode(reshape(data, 1, 1, :))
35+
# `Base.read`, not the generated cv::read wrappers that shadow `read` here.
36+
# The byte buffer is decoded via the `imdecode(::AbstractVector{UInt8})`
37+
# convenience method (issue #58).
38+
data = Base.read(stream(s))
39+
img = imdecode(data)
3740
return img
3841
end
3942

4043
function load(s::Stream{T}, flags::Int64) where {T<:_IMAGE_DATA_FORMATS}
41-
data = read(stream(s))
42-
img = imdecode(reshape(data, 1, 1, :), flags)
44+
data = Base.read(stream(s))
45+
img = imdecode(data, flags)
4346
return img
4447
end
4548

src/mat_conversion.jl

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,40 @@ CV_MAT_DEPTH(flags) = ((flags) & CV_MAT_DEPTH_MASK)
1616
CV_MAKETYPE(depth,cn) = (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
1717
CV_MAKE_TYPE = CV_MAKETYPE
1818

19+
# Bijective map between Julia element types and OpenCV depth constants. The two
20+
# conversion directions below both look this up rather than repeating a 7-branch
21+
# `if`/`elseif` ladder; adding a supported dtype is a single entry here.
22+
const _CV_DEPTH = (
23+
(UInt8, CV_8U),
24+
(Int8, CV_8S),
25+
(UInt16, CV_16U),
26+
(Int16, CV_16S),
27+
(Int32, CV_32S),
28+
(Float32, CV_32F),
29+
(Float64, CV_64F),
30+
)
31+
32+
# OpenCV depth constant for a Julia element type (throws on unsupported types).
33+
function _cv_depth(::Type{T}) where {T}
34+
for (jt, depth) in _CV_DEPTH
35+
jt === T && return depth
36+
end
37+
throw(OpenCVError("unsupported array element type $T for cv::Mat; expected one " *
38+
"of UInt8, Int8, UInt16, Int16, Int32, Float32, Float64"))
39+
end
40+
41+
# Julia element type for an OpenCV type flag + channel count (throws on unknown flag).
42+
function _julia_eltype(cvtype, channels)
43+
for (jt, depth) in _CV_DEPTH
44+
cvtype == CV_MAKE_TYPE(depth, channels) && return jt
45+
end
46+
throw(OpenCVError("unsupported cv::Mat type returned from OpenCV " *
47+
"(type flag $cvtype, $channels channel(s))"))
48+
end
49+
1950
function cpp_to_julia(mat::CxxMat)
2051
rets = jlopencv_core_Mat_mutable_data(mat)
21-
if rets[2] == CV_MAKE_TYPE(CV_8U, rets[3])
22-
dtype = UInt8
23-
elseif rets[2]==CV_MAKE_TYPE(CV_8S, rets[3])
24-
dtype = Int8
25-
elseif rets[2]==CV_MAKE_TYPE(CV_16U, rets[3])
26-
dtype = UInt16
27-
elseif rets[2]==CV_MAKE_TYPE(CV_16S, rets[3])
28-
dtype = Int16
29-
elseif rets[2]==CV_MAKE_TYPE(CV_32S, rets[3])
30-
dtype = Int32
31-
elseif rets[2]==CV_MAKE_TYPE(CV_32F, rets[3])
32-
dtype = Float32
33-
elseif rets[2]==CV_MAKE_TYPE(CV_64F, rets[3])
34-
dtype = Float64
35-
else
36-
error("Bad type returned from OpenCV")
37-
end
52+
dtype = _julia_eltype(rets[2], rets[3])
3853
steps = [rets[6]/sizeof(dtype), rets[7]/sizeof(dtype)]
3954
# println(steps[1]/rets[3], steps[2]/rets[3]/rets[4])
4055
#TODO: Implement views when steps do not result in continous memory
@@ -83,21 +98,8 @@ function julia_to_cpp(img::InputArray)
8398

8499
push!(ndims_a, Int32(size(img)[3]))
85100
push!(ndims_a, Int32(size(img)[2]))
86-
if eltype(img) == UInt8
87-
return CxxMat(2, pointer(ndims_a), CV_MAKE_TYPE(CV_8U, size(img)[1]), Ptr{Nothing}(pointer(img)), pointer(steps_a))
88-
elseif eltype(img) == UInt16
89-
return CxxMat(2, pointer(ndims_a), CV_MAKE_TYPE(CV_16U, size(img)[1]), Ptr{Nothing}(pointer(img)), pointer(steps_a))
90-
elseif eltype(img) == Int8
91-
return CxxMat(2, pointer(ndims_a), CV_MAKE_TYPE(CV_8S, size(img)[1]), Ptr{Nothing}(pointer(img)), pointer(steps_a))
92-
elseif eltype(img) == Int16
93-
return CxxMat(2, pointer(ndims_a), CV_MAKE_TYPE(CV_16S, size(img)[1]), Ptr{Nothing}(pointer(img)), pointer(steps_a))
94-
elseif eltype(img) == Int32
95-
return CxxMat(2, pointer(ndims_a), CV_MAKE_TYPE(CV_32S, size(img)[1]), Ptr{Nothing}(pointer(img)), pointer(steps_a))
96-
elseif eltype(img) == Float32
97-
return CxxMat(2, pointer(ndims_a), CV_MAKE_TYPE(CV_32F, size(img)[1]), Ptr{Nothing}(pointer(img)), pointer(steps_a))
98-
elseif eltype(img) == Float64
99-
return CxxMat(2, pointer(ndims_a), CV_MAKE_TYPE(CV_64F, size(img)[1]), Ptr{Nothing}(pointer(img)), pointer(steps_a))
100-
end
101+
cvtype = CV_MAKE_TYPE(_cv_depth(eltype(img)), size(img)[1])
102+
return CxxMat(2, pointer(ndims_a), cvtype, Ptr{Nothing}(pointer(img)), pointer(steps_a))
101103
else
102104
# Copy array, invalid config
103105
return julia_to_cpp(img[:, :, :])

src/overrides/convenience.jl

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Hand-written convenience wrappers: GUI callbacks (which need closures GC-protected),
2+
# CascadeClassifier helpers, and the string form of VideoWriter_fourcc.
3+
4+
function createButton(bar_name::String, on_change, userdata, type::Int32 = 0, initial_button_state::Bool = false)
5+
func = (x)->on_change(x, userdata)
6+
CxxWrap.gcprotect(userdata)
7+
CxxWrap.gcprotect(func)
8+
CxxWrap.gcprotect(on_change)
9+
return jl_cpp_cv2.createButton(bar_name,func, type, initial_button_state)
10+
end
11+
12+
function setMouseCallback(winname::String, onMouse, userdata)
13+
func = (event, x, y, flags)->onMouse(event, x, y, flags, userdata)
14+
CxxWrap.gcprotect(userdata)
15+
CxxWrap.gcprotect(func)
16+
CxxWrap.gcprotect(onMouse)
17+
return jl_cpp_cv2.setMouseCallback(winname,func)
18+
end
19+
20+
function createTrackbar(trackbarname::String, winname::String, value::Ref{Int32}, count::Int32, onChange, userdata)
21+
func = (x)->onChange(x, userdata)
22+
CxxWrap.gcprotect(userdata)
23+
CxxWrap.gcprotect(func)
24+
CxxWrap.gcprotect(onChange)
25+
return jl_cpp_cv2.createTrackbar(trackbarname, winname, value, count, func)
26+
end
27+
28+
function CascadeClassifier(filename::String)
29+
return cpp_to_julia(jlopencv_cv_cv_CascadeClassifier_cv_CascadeClassifier_CascadeClassifier(julia_to_cpp(filename)))
30+
end
31+
32+
33+
function detectMultiScale(cobj::CascadeClassifier, image::InputArray, scaleFactor::Float64, minNeighbors::Int32, flags::Int32, minSize::Size{Int32}, maxSize::Size{Int32})
34+
return cpp_to_julia(jlopencv_cv_cv_CascadeClassifier_cv_CascadeClassifier_detectMultiScale(julia_to_cpp(cobj),julia_to_cpp(image),julia_to_cpp(scaleFactor),julia_to_cpp(minNeighbors),julia_to_cpp(flags),julia_to_cpp(minSize),julia_to_cpp(maxSize)))
35+
end
36+
detectMultiScale(cobj::CascadeClassifier, image::InputArray; scaleFactor::Float64 = Float64(1.1), minNeighbors::Int32 = Int32(3), flags::Int32 = Int32(0), minSize::Size{Int32} = (Size{Int32}(0,0)), maxSize::Size{Int32} = (Size{Int32}(0,0))) = detectMultiScale(cobj, image, scaleFactor, minNeighbors, flags, minSize, maxSize)
37+
38+
function empty(cobj::CascadeClassifier)
39+
return cpp_to_julia(jlopencv_cv_cv_CascadeClassifier_cv_CascadeClassifier_empty(julia_to_cpp(cobj)))
40+
end
41+
42+
## Convenience: mirror Python's `cv.VideoWriter_fourcc(*"h264")`. Issue #31.
43+
function VideoWriter_fourcc(s::AbstractString)
44+
length(s) == 4 ||
45+
throw(ArgumentError("VideoWriter_fourcc expects a 4-character codec tag, got $(repr(s))"))
46+
VideoWriter_fourcc(s[1], s[2], s[3], s[4])
47+
end

0 commit comments

Comments
 (0)