Issue 1:
System information (version)

reproduced in linux (redhat 7.5 and ubuntu 16) on both intel and power architectures, on opencv versions 3.1 and 3.2

via the standard python binding of opencv

problem description

warpAffine (and possibly other functions) use very incorrect constant border value when the image has more than 4 channels.

reproducing the problem:

I've created a minimalistic self contained script to easily reproduce the problem:

https://gist.github.com/YoelShoshan/1c9976eb82b00fa27f0b0491c6093efc

import cv2
import numpy as np

def base_tens_stats(tens):
print(f'shape={tens.shape} min={tens.min()} mean={tens.mean()} max={tens.max()}')

x = np.random.randint(0,1020, (256,256,20)).astype(np.uint16)

base_tens_stats(x)

M = np.array([[ -9.86677170e-01, -3.59121144e-01, 4.28262177e+02],
[ 3.59121144e-01, -9.86677170e-01, 3.36327148e+02],
[ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]], dtype=np.float32)

y = cv2.warpAffine(x, M[:2,:], x.shape[:2],
borderMode=cv2.BORDER_CONSTANT,
borderValue=0,
flags=cv2.INTER_NEAREST)

print('after transform 1')
base_tens_stats(y)

y = cv2.warpAffine(x, M[:2,:], x.shape[:2],
borderMode=cv2.BORDER_CONSTANT,
borderValue=0,
flags=cv2.INTER_LINEAR)

print('after transform 2')
base_tens_stats(y)

here's an example output:

shape=(256, 256, 20) min=0 mean=509.25673599243163 max=1019
after transform 1
shape=(256, 256, 20) min=0 mean=10139.467961883545 max=62814
after transform 2
shape=(256, 256, 20) min=0 mean=138.16527404785157 max=1019

expected result

the printed "all max=..." should be max=1019, but instead it gets filled with value 62814 when the channels num is bigger than 4 !

Issue 2:
System information (version)

OpenCV => 3.2

Operating System / Platform => ubuntu 14.04

Compiler => GCC 4.8.2

Detailed description

When using warpPerspective with images with more than 4 channels, using BORDER_CONSTANT and one of the interpolation methods INTER_NEAREST, INTER_CUBIC or INTER_LANCZOS4, the borders are wrong.

Steps to reproduce

Call the following function using BORDER_CONSTANT and one of the interpolation methods above, pass at least one grayscale image and a nonunit transform:

void test(const std::vector<cv::Mat> &src_images, const cv::Matx33d &transform,
cv::Size img_size, int num_channels = 100, int interpolation = cv::INTER_NEAREST,
int border = cv::BORDER_CONSTANT, const cv::Scalar &value = cv::Scalar(255, 170, 85, 0))
{
std::vector<cv::Mat> channels(num_channels), channels_true(num_channels);

for (int i = 0; i < num_channels; ++i) {
int num = std::rand() % src_images.size();
channels[i] = src_images[num].clone();
channels_true[i] = channels[i].clone();
}
cv::Mat multichannel;
cv::merge(channels, multichannel);

for (size_t i = 0; i < channels_true.size(); ++i) {
cv::Scalar val = value[i % 4];
auto ch = channels_true[i];
cv::warpPerspective(ch, ch, transform, img_size, interpolation, border,
val);
}
cv::warpPerspective(multichannel, multichannel, transform,
img_size, interpolation, border, value);
cv::split(multichannel, channels);
for (size_t i = 0; i < channels.size(); ++i) {
double s = cv::sum(channels[i] != channels_true[i])[0];
if (s > 0.0) {
cv::imshow("Error", channels[i]);
cv::imshow("Should be", channels_true[i]);
cv::waitKey(0);
}
}
}