Multiple MinimumDistanceConstraint constraints - drake

I'm currently applying multiple MinimumDistanceConstraint constraints. I've modified this class so that I can apply filters to consider or ignore specific pairs of geometries, which enables me to apply different distance thresholds to different pairs. So instead of defining one single MinimumDistanceConstraint, I define multiple of these with different distance thresholds and geometry pair filters.
I'm seeing very strange behavior when I define some filters vs none at all. When I define no filters, the solver (SNOPT) correctly optimizes all geometry pairs away with the same distance threshold.
Now when I apply some filters to certain geometries in set A, SNOPT suddenly returns immediately with a solution without getting all the geometries out of collision. Looking at SNOPT's output file, I see that the nonlinear penalty function returned 0 when it should not be (since certain geometry pairs are still violating their distance constraints). When I call my_min_dist_constraint->CheckSatisfied(x) on these violated constraints, I correctly get a return value of false. I've double-checked that I haven't messed up gradient calculations. So how can the constraint return false, but the solver still return that all constraints are satisfied?
I've pasted my custom MinimumDistanceConstraint below. I've verified that the logic of the filters is correct, and that they are being considered in the Distances() function. When comparing the code with drake's own implementation, you'll see that the derivative and value computation are unchanged, I am only ignoring certain geometry pairs. The result is that I will have multiple nonlinear, mininimum_value constraints of smaller vector length as opposed to a single, large constraint.
[EDIT]
In the Distances function, I notice that right after calculating &distances(distance_count) from internal::CalcDistanceDerivatives(), its value is a reasonable scalar. However, when printing the entire distances at the end of the function, I get many nan values when they shouldn't be. Before calling distances.resize(distance_count), most of the values are still reasonable, so clearly the resize is causing the issue.
Here is how I would generally apply multiple distance constraints:
for (int cons_idx=0; cons_idx<distance_filters.size(); cons_idx++) {
auto constraint =
std::shared_ptr<MinimumDistanceConstraintCustom>(new MinimumDistanceConstraintCustom(
&plant_, minimum_distances.at(cons_idx), get_mutable_context(), distance_filters.at(cons_idx), {},
influence_distance_offset));
prog_->AddConstraint(constraint, q_);
// Finally, one more constraint for all the collision pairs that don't fit in any of the collision groups
auto constraint =
std::shared_ptr<MinimumDistanceConstraintCustom>(new MinimumDistanceConstraintCustom(
&plant_, complement_minimum_distance, get_mutable_context(), distance_filters, {},
influence_distance_offset));
}
Here is how MinimumDistanceConstraintCustom is defined:
#include "control/drake_library/minimum_distance_constraint.h"
#include <limits>
#include <vector>
#include <Eigen/Dense>
#include "drake/multibody/inverse_kinematics/distance_constraint_utilities.h"
#include "drake/multibody/inverse_kinematics/kinematic_evaluator_utilities.h"
#include <string>
#include <iostream>
namespace drake {
namespace multibody {
using internal::RefFromPtrOrThrow;
using namespace std;
template <typename T, typename S>
VectorX<S> Distances(const MultibodyPlant<T>& plant,
systems::Context<T>* context,
const Eigen::Ref<const VectorX<S>>& q,
double influence_distance,
double minimum_distance,
const std::unordered_set<GeomPair, min_dist_pair_hash>& considered_pairs) {
internal::UpdateContextConfiguration(context, plant, q);
const auto& query_port = plant.get_geometry_query_input_port();
if (!query_port.HasValue(*context)) {
throw std::invalid_argument(
"MinimumDistanceConstraintCustom: Cannot get a valid geometry::QueryObject. "
"Either the plant geometry_query_input_port() is not properly "
"connected to the SceneGraph's output port, or the plant_context_ is "
"incorrect. Please refer to AddMultibodyPlantSceneGraph on connecting "
"MultibodyPlant to SceneGraph.");
}
const auto& query_object =
query_port.template Eval<geometry::QueryObject<T>>(*context);
const geometry::SceneGraphInspector<T>& inspector =
query_object.inspector();
// Get all candidate collision geometry pairs
// NOTE: querying all distances, then selecting is faster than looping and querying distance for each individual pair
const std::vector<geometry::SignedDistancePair<T>> signed_distance_pairs =
query_object.ComputeSignedDistancePairwiseClosestPoints(
influence_distance);
VectorX<S> distances(signed_distance_pairs.size());
int distance_count{0};
cout << "Distances! " << endl;
for (const auto& signed_distance_pair : signed_distance_pairs) {
const GeomPair p1 {signed_distance_pair.id_A, signed_distance_pair.id_B};
const GeomPair p2 {signed_distance_pair.id_B, signed_distance_pair.id_A};
bool is_considered = (
(considered_pairs.find(p1) != considered_pairs.end()) ||
(considered_pairs.find(p2) != considered_pairs.end())
);
if (is_considered && signed_distance_pair.distance < influence_distance) {
// cout << "considering " << inspector.GetName(signed_distance_pair.id_A) << "(" << signed_distance_pair.id_A << ")" << ", ";
// cout << inspector.GetName(signed_distance_pair.id_B) << "(" << signed_distance_pair.id_B << ")";
// cout << ": dist " << signed_distance_pair.distance << " vs influence dist " << influence_distance << " vs minimum_distance " << minimum_distance << endl;
const geometry::FrameId frame_A_id =
inspector.GetFrameId(signed_distance_pair.id_A);
const geometry::FrameId frame_B_id =
inspector.GetFrameId(signed_distance_pair.id_B);
const Frame<T>& frameA =
plant.GetBodyFromFrameId(frame_A_id)->body_frame();
const Frame<T>& frameB =
plant.GetBodyFromFrameId(frame_B_id)->body_frame();
internal::CalcDistanceDerivatives(
plant, *context, frameA, frameB,
// GetPoseInFrame() returns RigidTransform<double> -- we can't
// multiply across heterogeneous scalar types; so we cast the double
// to T.
inspector.GetPoseInFrame(signed_distance_pair.id_A)
.template cast<T>() *
signed_distance_pair.p_ACa,
signed_distance_pair.distance, signed_distance_pair.nhat_BA_W, q,
&distances(distance_count++));
}
}
distances.resize(distance_count);
return distances;
}
template <typename T>
void MinimumDistanceConstraintCustom::Initialize(
const MultibodyPlant<T>& plant, systems::Context<T>* plant_context,
double minimum_distance, double influence_distance_offset,
MinimumDistancePenaltyFunction penalty_function,
const std::vector<MinimumDistanceConstraintFilter> filters, const bool complement_all_filters,
std::unordered_set<GeomPair, min_dist_pair_hash>& considered_pairs) {
CheckPlantIsConnectedToSceneGraph(plant, *plant_context);
if (!std::isfinite(influence_distance_offset)) {
throw std::invalid_argument(
"MinimumDistanceConstraintCustom: influence_distance_offset must be finite.");
}
if (influence_distance_offset <= 0) {
throw std::invalid_argument(
"MinimumDistanceConstraintCustom: influence_distance_offset must be "
"positive.");
}
const auto& query_port = plant.get_geometry_query_input_port();
// Maximum number of SignedDistancePairs returned by calls to
// ComputeSignedDistancePairwiseClosestPoints().
const auto all_collision_candidates =
query_port.template Eval<geometry::QueryObject<T>>(*plant_context)
.inspector()
.GetCollisionCandidates();
if (filters.size() > 0) {
for (const GeomPair& geom_pair : all_collision_candidates) {
if (min_dist_should_include(geom_pair, filters, complement_all_filters)) {
considered_pairs.insert(geom_pair);
}
}
} else {
std::copy(all_collision_candidates.begin(),
all_collision_candidates.end(),
std::inserter(considered_pairs, considered_pairs.begin()));
}
minimum_value_constraint_ = std::make_unique<solvers::MinimumValueConstraint>(
this->num_vars(), minimum_distance, influence_distance_offset,
all_collision_candidates.size(),
[&plant, plant_context, &considered_pairs, minimum_distance](const auto& x, double influence_distance) {
return Distances<T, AutoDiffXd>(plant, plant_context, x,
influence_distance, minimum_distance, considered_pairs);
},
[&plant, plant_context, &considered_pairs, minimum_distance](const auto& x, double influence_distance) {
return Distances<T, double>(plant, plant_context, x,
influence_distance, minimum_distance, considered_pairs);
});
this->set_bounds(minimum_value_constraint_->lower_bound(),
minimum_value_constraint_->upper_bound());
if (penalty_function) {
minimum_value_constraint_->set_penalty_function(penalty_function);
}
}
MinimumDistanceConstraintCustom::MinimumDistanceConstraintCustom(
const multibody::MultibodyPlant<double>* const plant,
double minimum_distance, systems::Context<double>* plant_context,
MinimumDistancePenaltyFunction penalty_function,
double influence_distance_offset)
: solvers::Constraint(1, RefFromPtrOrThrow(plant).num_positions(),
Vector1d(0), Vector1d(0)),
plant_double_{plant},
plant_context_double_{plant_context},
plant_autodiff_{nullptr},
plant_context_autodiff_{nullptr} {
Initialize(*plant_double_, plant_context_double_, minimum_distance,
influence_distance_offset, penalty_function,
{},
false, /*Doesn't matter*/
considered_pairs_);
}
MinimumDistanceConstraintCustom::MinimumDistanceConstraintCustom(
const multibody::MultibodyPlant<AutoDiffXd>* const plant,
double minimum_distance, systems::Context<AutoDiffXd>* plant_context,
MinimumDistancePenaltyFunction penalty_function,
double influence_distance_offset)
: solvers::Constraint(1, RefFromPtrOrThrow(plant).num_positions(),
Vector1d(0), Vector1d(0)),
plant_double_{nullptr},
plant_context_double_{nullptr},
plant_autodiff_{plant},
plant_context_autodiff_{plant_context} {
Initialize(*plant_autodiff_, plant_context_autodiff_, minimum_distance,
influence_distance_offset, penalty_function,
{},
false, /*Doesn't matter*/
considered_pairs_);
}
MinimumDistanceConstraintCustom::MinimumDistanceConstraintCustom(
const multibody::MultibodyPlant<double>* const plant,
double minimum_distance, systems::Context<double>* plant_context,
const MinimumDistanceConstraintFilter filter,
MinimumDistancePenaltyFunction penalty_function,
double influence_distance_offset)
: solvers::Constraint(1, RefFromPtrOrThrow(plant).num_positions(),
Vector1d(0), Vector1d(0)),
plant_double_{plant},
plant_context_double_{plant_context},
plant_autodiff_{nullptr},
plant_context_autodiff_{nullptr} {
Initialize(*plant_double_, plant_context_double_, minimum_distance,
influence_distance_offset, penalty_function, {filter}, false, considered_pairs_);
}
MinimumDistanceConstraintCustom::MinimumDistanceConstraintCustom(
const multibody::MultibodyPlant<double>* const plant,
double minimum_distance, systems::Context<double>* plant_context,
const std::vector<MinimumDistanceConstraintFilter> complement_filters,
MinimumDistancePenaltyFunction penalty_function,
double influence_distance_offset)
: solvers::Constraint(1, RefFromPtrOrThrow(plant).num_positions(),
Vector1d(0), Vector1d(0)),
plant_double_{plant},
plant_context_double_{plant_context},
plant_autodiff_{nullptr},
plant_context_autodiff_{nullptr} {
Initialize(*plant_double_, plant_context_double_, minimum_distance,
influence_distance_offset, penalty_function,
complement_filters, true, considered_pairs_);
}
template <typename T>
void MinimumDistanceConstraintCustom::DoEvalGeneric(
const Eigen::Ref<const VectorX<T>>& x, VectorX<T>* y) const {
minimum_value_constraint_->Eval(x, y);
}
void MinimumDistanceConstraintCustom::DoEval(
const Eigen::Ref<const Eigen::VectorXd>& x, Eigen::VectorXd* y) const {
DoEvalGeneric(x, y);
}
void MinimumDistanceConstraintCustom::DoEval(const Eigen::Ref<const AutoDiffVecXd>& x,
AutoDiffVecXd* y) const {
DoEvalGeneric(x, y);
}
} // namespace multibody
} // namespace drake

SNOPT suddenly returns immediately with a solution without getting all the geometries out of collision.
First I would suggest to evaluate your constraint at this solution. You could do it through
std::cout << "constraint value: " << result.EvalBinding(constraint).transpose() << "\n";
std::cout << "constraint lower bound: " << constraint.evaluator()->lower_bound().transpose() << "\nconstraint upper bound: " << constraint.evaluator()>upper_bound().transpose() << "\n";
I think you will see that the constraint value is indeed between the lower bound and the upper bound at the solution. This suggest that the problem is in how you evaluate your constraint.
From a quick glance of your code, I believe the issue is in this line
distances.resize(distance_count);
in your Distance function. Check Eigen's documentation on resize function. Specifically it says
The resize() method is a no-operation if the actual matrix size doesn't change; otherwise it is destructive: the values of the coefficients may change.
Since you change the size of distances, after calling resize your distances vector is not meaningful.
I would suggest to use conservativeResize() function instead of resize.

Related

About extending a Look Up Table at compile time

I'd like to extend my instrumental Profiler in order to avoid it affect too much performances.
Im my current implementation, I'm using a ProfilerHelper taking one string, which is put whereever you want in the profiling f().
The ctor is starting the measurement and the dector is closing it, logging the Delta in an unordered_map entry, which is key is the string.
Now, I'd like to turn all of that into a faster stuff.
First of all, I'd like to create a string LUT (Look Up Table) contaning the f()s names at compile time, and turn the unordered_map to a plain vector which is paired by the string function LUT.
Now the question is: I've managed to create a LUT but std::string_view, but I cannot find a way to extend it at compile time.
A first rought trial sounds like this:
template<unsigned N>
constexpr auto LUT() {
std::array<std::string_view, N> Strs{};
for (unsigned n = 0; n < N; n++) {
Strs[n] = "";
}
return Strs;
};
constexpr std::array<std::string_view, 0> StringsLUT { LUT<0>() };
constexpr auto AddString(std::string_view const& Str)
{
constexpr auto Size = StringsLUT.size();
std::array<std::string_view, Size + 1> Copy{};
for (auto i = 0; i < Size; ++i)
Copy[i] = StringsLUT[i];
Copy[Size] = Str;
return Copy;
};
int main()
{
constexpr auto Strs = AddString(__builtin_FUNCTION());
//for (auto const Str : Strs)
std::cout << Strs[0] << std::endl;
}
So my idea should be to recall the AddString whenever needed in my f()s to be profiled, extending this list at compile time.
But of course I should take the returned Copy and replace the StringsLUT everytime, to land to a final StringsLUT with all the f() names inside it.
Is there a way to do that at compile time?
Sorry, but I'm just entering the magic "new" world of constexpr applied to LUT right in these days.
Tx for your support in advance.

Label Images in OpenCV

Given the same binary input image (up to inversion), is there a guarantee that the labels in the images returned by all the following functions are all consistent?
cv::distanceTransform() (version with labels)
cv::connectedComponents()
cv::connectedComponentsWithStats()
Does this appear in the docs anywhere?
If you pass to distanceTransform a binary image inverted with respect to connectedComponents or connectedComponentsWithStats, the labels are computed the same way and will be consistent.
I was not able to find any reference in the doc, but the labels will be computed by the same algorithm (connectedComponents_sub1) in all cases.
connectedComponents[WithStats]
int cv::connectedComponents(InputArray _img, OutputArray _labels, int connectivity, int ltype){
const cv::Mat img = _img.getMat();
_labels.create(img.size(), CV_MAT_DEPTH(ltype));
cv::Mat labels = _labels.getMat();
connectedcomponents::NoOp sop;
if(ltype == CV_16U){
return connectedComponents_sub1(img, labels, connectivity, sop);
}else if(ltype == CV_32S){
return connectedComponents_sub1(img, labels, connectivity, sop);
}else{
CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s");
return 0;
}
}
int cv::connectedComponentsWithStats(InputArray _img, OutputArray _labels, OutputArray statsv,
OutputArray centroids, int connectivity, int ltype)
{
const cv::Mat img = _img.getMat();
_labels.create(img.size(), CV_MAT_DEPTH(ltype));
cv::Mat labels = _labels.getMat();
connectedcomponents::CCStatsOp sop(statsv, centroids);
if(ltype == CV_16U){
return connectedComponents_sub1(img, labels, connectivity, sop);
}else if(ltype == CV_32S){
return connectedComponents_sub1(img, labels, connectivity, sop);
}else{
CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s");
return 0;
}
}
As you can see, the labeling part is performed by the connectedComponents_sub1 function in both cases. The only difference between the two is the statistic computation: connectedcomponents::NoOp versus connectedcomponents::CCStatsOp, not relevant for label computation.
distanceTransform
void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labels,
int distType, int maskSize, int labelType )
{
...
if( labelType == CV_DIST_LABEL_CCOMP )
{
Mat zpix = src == 0;
connectedComponents(zpix, labels, 8, CV_32S);
}
...
}
The labels are computed internally by the function connectedComponents.

OpenCV cv::Mat set if

Is there a simple way to set all values in a cv::Mat to a given value if they fulfill some condition. For instance, I have CV_32FC1, and I want set all values which are 0 to 20. In MATLAB I would have simply done this:
M(M == 0) = 20;
You can use
cv::Mat mask = M == 0;
M.setTo(0.5, mask);
However, it includes using additional memory for creating mask, but is a solution using opencv API therefore can be applied to all matrix types. If you consider performance issues, you can always refer directly to Mat::data to optimize this solution for concrete matrix type.
This is a classic case for look-up table. It is fast, simple, and can remap multiple values at same time.
Thanks to #marol 's comments, I settled for the implementation below. I am using C++11 lambda functions to condition which values need to be changed. To demonstrate its power, my condition is to set to DEFAULT_VAL when the value is out of the range [MIN_VAL, MAX_VAL]:
#include <functional>
#define MatType float
#define MatCmpFunc std::function<bool(const MatType&)>
.
.
.
// function which accepts lambda function to condition values which need to
// be changed
void MatSetIf(cv::Mat& inputmat, const MatType& newval, MatCmpFunc func) {
float* pmat = (float*)inputmat.data;
// iterate and set only values which fulfill the criteria
for (int idx = 0; idx < inputmat.total(); ++idx) {
if (func(pmat[idx])) {
pmat[idx] = newval;
}
}
}
.
.
.
void main() {
cv::Mat mymat(100,100,CV_32FC1);
const float MIN_VAL = 10;
const float MAX_VAL = 1000;
const float DEFAULT_VAL = -1;
.
.
.
// declare lambda function which returns true when mat value out of range
MatCmpFunc func = [&](const DepthMatType& val) -> bool {
return (val < MIN_VAL || val > MAX_VAL) ? true : false;
};
// use lambda func above to set all out of range values to 50
Mat32FSetIf(mymat, DEFAULT_VAL, func);
.
.
.
}

Is there public key initialization API with point compression?

I am tumbling around with CryptoPP and cannot find answer to this specific question. Here is sample source code (partial)
AutoSeededRandomPool prng;
//Generate a private key
ECDSA<ECP, CryptoPP::SHA256>::PrivateKey privateKey;
privateKey.Initialize(prng, CryptoPP::ASN1::secp256r1());
// Generate publicKey
ECDSA<ECP, CryptoPP::SHA256>::PublicKey publicKey;
privateKey.MakePublicKey(publicKey);
// Extract Component values
Integer p = privateKey.GetGroupParameters().GetCurve().GetField().GetModulus();
Integer a = privateKey.GetGroupParameters().GetCurve().GetA();
Integer b = privateKey.GetGroupParameters().GetCurve().GetB();
Integer Gx = privateKey.GetGroupParameters().GetSubgroupGenerator().x;
Integer Gy = privateKey.GetGroupParameters().GetSubgroupGenerator().y;
Integer n = privateKey.GetGroupParameters().GetSubgroupOrder();
Integer h = privateKey.GetGroupParameters().GetCofactor();
Integer Qx = publicKey.GetPublicElement().x;
Integer Qy = publicKey.GetPublicElement().y;
Integer x = privateKey.GetPrivateExponent();
// Construct Point elelemt;
ECP curve(p,a,b);
ECP::Point G(Gx,Gy);
ECP::Point Q(Qx,Qy);
//Build publicKey using elements (no point compression)
ECDSA<ECP, CryptoPP::SHA256>::PublicKey GeneratedPublicKey;
GeneratedPublicKey.Initialize(curve,G,n,Q);
assert(GeneratedPublicKey.Validate(prng, 3));
//Build publicKey using elements (with point compression)?
With this way, I can generate publicKey using component values. However, I cannot
make it work with point compression-which means I don't have Qy value- Is there a
way to do it? Initialize method has two overloading but none of them are for point
compression situation.
My question is specific with Crypto++ on "PublicKey.Initialize(curve,G,n,Q)". Since I cannot transfer whole publicKey with my current project-which I am force to specify domain
parameter as index value and can only transfer Qx value. So I should initialize publicKey
using something like "PublicKey.Initialize(curve,G,n,Q)" However, I cannot find such initialization API concerning point compression.
So, this is not about "how to do a point compression" but "Is there a way to initialize
public key without having Qy value?"
How to Construct ECDSA publicKey using only with x value (Point compression)?
x is the private exponent. The public key is a point on the curve; and it does not use the private exponent.
To get the public key: take the private exponent, and raise your base point to it. That is, Q = G^x.
If you want to set the private exponent on a private key or decryptor, then set the domain parameters (i.e., DL_GroupParameters_EC< ECP > or DL_GroupParameters_EC< EC2M >) and then call SetPrivateExponent(x);.
Have you reviewed your previous question at How can I recover compressed y value from sender?? The community took the time to provide you with an answer and sample code, but you did not acknowledge or follow up.
I think owlstead said it best here:
Why would we care answer you if you are not inclined to accept answers
or even follow up to them? Your questions are all right, but the way
you treat the community is terrible.
"Is there a way to initialize public key without having Qy value?"
Yes, there is. Here is an crypto++ example:
#include <string>
#include <iostream>
#include <cryptopp/cryptlib.h>
#include <cryptopp/ecp.h>
#include <cryptopp/eccrypto.h>
#include <cryptopp/hex.h>
#include <cryptopp/oids.h>
#include <cryptopp/osrng.h>
using namespace CryptoPP;
using std::cout;
using std::endl;
int main()
{
OID curve = ASN1::secp256r1();
ECDH<ECP>::Domain domain(curve);
SecByteBlock privKey(domain.PrivateKeyLength());
SecByteBlock pubKey(domain.PublicKeyLength());
AutoSeededRandomPool prng;
domain.GenerateKeyPair(prng, privKey, pubKey);
// Convert public key to string representation
std::string pub_str;
HexEncoder encoder;
encoder.Attach( new StringSink(pub_str) );
encoder.Put( pubKey.data(), pubKey.size() );
encoder.MessageEnd();
// Uncompressed point - first byte '04' in front of the string.
std::cout << "Uncompressed public key (point) " << pub_str << endl;
// Extract x value from the point
std::string public_point_x = pub_str.substr(2, 64);
// Compressed - '02' byte in front of the string.
public_point_x = "02" + public_point_x;
std::cout << "Compressed public key (point) " << public_point_x << endl;
// ----- reconstruct point from compressed point/value.
StringSource ss(public_point_x, true, new HexDecoder);
ECP::Point point;
domain.GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());
cout << "Result after decompression X: " << std::hex << point.x << endl;
cout << "Result after decompression Y: " << std::hex << point.y << endl;
return 0;
}
I hope this is the answer to your question. I was using ECDH, but it should work equally well with ECDSA class.

Print cv::Mat opencv

I am trying to print cv::Mat which contains my image. However whenever I print the Mat using cout, a 2D array printed into my text file. I want to print one one pixel in one line only. How can i print line wise pixels from cv::Mat.
A generic for_each loop, you could use it to print your data
/**
*#brief implement details of for_each_channel, user should not use this function
*/
template<typename T, typename UnaryFunc>
UnaryFunc for_each_channel_impl(cv::Mat &input, int channel, UnaryFunc func)
{
int const rows = input.rows;
int const cols = input.cols;
int const channels = input.channels();
for(int row = 0; row != rows; ++row){
auto *input_ptr = input.ptr<T>(row) + channel;
for(int col = 0; col != cols; ++col){
func(*input_ptr);
input_ptr += channels;
}
}
return func;
}
use it like
for_each_channel_impl<uchar>(input, 0, [](uchar a){ std::cout<<(size_t)a<<", "; });
you could do some optimization to continuous channel, then it may looks like
/**
*#brief apply stl like for_each algorithm on a channel
*
* #param
* T : the type of the channel(ex, uchar, float, double and so on)
* #param
* channel : the channel need to apply for_each algorithm
* #param
* func : Unary function that accepts an element in the range as argument
*
*#return :
* return func
*/
template<typename T, typename UnaryFunc>
inline UnaryFunc for_each_channel(cv::Mat &input, int channel, UnaryFunc func)
{
if(input.channels() == 1 && input.isContinuous()){
return for_each_continuous_channels<T>(input, func);
}else{
return for_each_channel_impl<T>(input, channel, func);
}
}
This kind of generic loopsave me a lot of times, I hope you find it helpful.If there are
any bugs, or you have better idea, please tell me.
I would like to design some generic algorithms for opencl too, sadly it do not support
template, I hope one day CUDA will become an open standard, or opencl will support template.
This works for any number of channels as long as the channels type are base on byte, non-byte
channel may not work.

Resources