mirror of
https://github.com/opencv/opencv.git
synced 2024-11-28 21:20:18 +08:00
Merge pull request #12116 from luzpaz:misc-typos
This commit is contained in:
commit
47e3e89e30
@ -16,7 +16,7 @@ void calib::Euler(const cv::Mat& src, cv::Mat& dst, int argType)
|
||||
{
|
||||
if((src.rows == 3) && (src.cols == 3))
|
||||
{
|
||||
//convert rotaion matrix to 3 angles (pitch, yaw, roll)
|
||||
//convert rotation matrix to 3 angles (pitch, yaw, roll)
|
||||
dst = cv::Mat(3, 1, CV_64F);
|
||||
double pitch, yaw, roll;
|
||||
|
||||
@ -55,7 +55,7 @@ void calib::Euler(const cv::Mat& src, cv::Mat& dst, int argType)
|
||||
else if( (src.cols == 1 && src.rows == 3) ||
|
||||
(src.cols == 3 && src.rows == 1 ) )
|
||||
{
|
||||
//convert vector which contains 3 angles (pitch, yaw, roll) to rotaion matrix
|
||||
//convert vector which contains 3 angles (pitch, yaw, roll) to rotation matrix
|
||||
double pitch, yaw, roll;
|
||||
if(src.cols == 1 && src.rows == 3)
|
||||
{
|
||||
|
@ -141,7 +141,7 @@
|
||||
# -- Same as CUDA_ADD_EXECUTABLE except that a library is created.
|
||||
#
|
||||
# CUDA_BUILD_CLEAN_TARGET()
|
||||
# -- Creates a convience target that deletes all the dependency files
|
||||
# -- Creates a convenience target that deletes all the dependency files
|
||||
# generated. You should make clean after running this target to ensure the
|
||||
# dependency files get regenerated.
|
||||
#
|
||||
@ -473,7 +473,7 @@ else()
|
||||
endif()
|
||||
|
||||
# Propagate the host flags to the host compiler via -Xcompiler
|
||||
option(CUDA_PROPAGATE_HOST_FLAGS "Propage C/CXX_FLAGS and friends to the host compiler via -Xcompile" ON)
|
||||
option(CUDA_PROPAGATE_HOST_FLAGS "Propagate C/CXX_FLAGS and friends to the host compiler via -Xcompile" ON)
|
||||
|
||||
# Enable CUDA_SEPARABLE_COMPILATION
|
||||
option(CUDA_SEPARABLE_COMPILATION "Compile CUDA objects with separable compilation enabled. Requires CUDA 5.0+" OFF)
|
||||
|
@ -362,7 +362,7 @@ MACRO(ADD_NATIVE_PRECOMPILED_HEADER _targetName _input)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
#also inlude ${oldProps} to have the same compile options
|
||||
#also include ${oldProps} to have the same compile options
|
||||
GET_TARGET_PROPERTY(oldProps ${_targetName} COMPILE_FLAGS)
|
||||
if (oldProps MATCHES NOTFOUND)
|
||||
SET(oldProps "")
|
||||
|
@ -260,7 +260,7 @@ endif()
|
||||
set(OpenCV_LIBRARIES ${OpenCV_LIBS})
|
||||
|
||||
#
|
||||
# Some macroses for samples
|
||||
# Some macros for samples
|
||||
#
|
||||
macro(ocv_check_dependencies)
|
||||
set(OCV_DEPENDENCIES_FOUND TRUE)
|
||||
|
@ -29,7 +29,7 @@ What happens in background ?
|
||||
objects). Everything inside rectangle is unknown. Similarly any user input specifying
|
||||
foreground and background are considered as hard-labelling which means they won't change in
|
||||
the process.
|
||||
- Computer does an initial labelling depeding on the data we gave. It labels the foreground and
|
||||
- Computer does an initial labelling depending on the data we gave. It labels the foreground and
|
||||
background pixels (or it hard-labels)
|
||||
- Now a Gaussian Mixture Model(GMM) is used to model the foreground and background.
|
||||
- Depending on the data we gave, GMM learns and create new pixel distribution. That is, the
|
||||
|
@ -129,7 +129,7 @@ function onOpenCvReady() {
|
||||
</html>
|
||||
@endcode
|
||||
|
||||
@note You have to call delete method of cv.Mat to free memory allocated in Emscripten's heap. Please refer to [Memeory management of Emscripten](https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#memory-management) for details.
|
||||
@note You have to call delete method of cv.Mat to free memory allocated in Emscripten's heap. Please refer to [Memory management of Emscripten](https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#memory-management) for details.
|
||||
|
||||
Try it
|
||||
------
|
||||
|
@ -37,7 +37,7 @@ So what happens in background ?
|
||||
objects). Everything inside rectangle is unknown. Similarly any user input specifying
|
||||
foreground and background are considered as hard-labelling which means they won't change in
|
||||
the process.
|
||||
- Computer does an initial labelling depeding on the data we gave. It labels the foreground and
|
||||
- Computer does an initial labelling depending on the data we gave. It labels the foreground and
|
||||
background pixels (or it hard-labels)
|
||||
- Now a Gaussian Mixture Model(GMM) is used to model the foreground and background.
|
||||
- Depending on the data we gave, GMM learns and create new pixel distribution. That is, the
|
||||
|
@ -16,7 +16,7 @@ In this tutorial is explained how to build a real time application to estimate t
|
||||
order to track a textured object with six degrees of freedom given a 2D image and its 3D textured
|
||||
model.
|
||||
|
||||
The application will have the followings parts:
|
||||
The application will have the following parts:
|
||||
|
||||
- Read 3D textured object model and object mesh.
|
||||
- Take input from Camera or Video.
|
||||
@ -426,16 +426,16 @@ Here is explained in detail the code for the real time application:
|
||||
@endcode
|
||||
OpenCV provides four PnP methods: ITERATIVE, EPNP, P3P and DLS. Depending on the application type,
|
||||
the estimation method will be different. In the case that we want to make a real time application,
|
||||
the more suitable methods are EPNP and P3P due to that are faster than ITERATIVE and DLS at
|
||||
the more suitable methods are EPNP and P3P since they are faster than ITERATIVE and DLS at
|
||||
finding an optimal solution. However, EPNP and P3P are not especially robust in front of planar
|
||||
surfaces and sometimes the pose estimation seems to have a mirror effect. Therefore, in this this
|
||||
tutorial is used ITERATIVE method due to the object to be detected has planar surfaces.
|
||||
surfaces and sometimes the pose estimation seems to have a mirror effect. Therefore, in this
|
||||
tutorial an ITERATIVE method is used due to the object to be detected has planar surfaces.
|
||||
|
||||
The OpenCV RANSAC implementation wants you to provide three parameters: the maximum number of
|
||||
iterations until stop the algorithm, the maximum allowed distance between the observed and
|
||||
computed point projections to consider it an inlier and the confidence to obtain a good result.
|
||||
The OpenCV RANSAC implementation wants you to provide three parameters: 1) the maximum number of
|
||||
iterations until the algorithm stops, 2) the maximum allowed distance between the observed and
|
||||
computed point projections to consider it an inlier and 3) the confidence to obtain a good result.
|
||||
You can tune these parameters in order to improve your algorithm performance. Increasing the
|
||||
number of iterations you will have a more accurate solution, but will take more time to find a
|
||||
number of iterations will have a more accurate solution, but will take more time to find a
|
||||
solution. Increasing the reprojection error will reduce the computation time, but your solution
|
||||
will be unaccurate. Decreasing the confidence your algorithm will be faster, but the obtained
|
||||
solution will be unaccurate.
|
||||
|
@ -46,7 +46,7 @@ cd /c/lib
|
||||
myRepo=$(pwd)
|
||||
CMAKE_CONFIG_GENERATOR="Visual Studio 14 2015 Win64"
|
||||
if [ ! -d "$myRepo/opencv" ]; then
|
||||
echo "clonning opencv"
|
||||
echo "cloning opencv"
|
||||
git clone https://github.com/opencv/opencv.git
|
||||
mkdir Build
|
||||
mkdir Build/opencv
|
||||
@ -58,7 +58,7 @@ else
|
||||
cd ..
|
||||
fi
|
||||
if [ ! -d "$myRepo/opencv_contrib" ]; then
|
||||
echo "clonning opencv_contrib"
|
||||
echo "cloning opencv_contrib"
|
||||
git clone https://github.com/opencv/opencv_contrib.git
|
||||
mkdir Build
|
||||
mkdir Build/opencv_contrib
|
||||
|
@ -198,7 +198,7 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename )
|
||||
|
||||
if( !fs.isOpened() || board_list.empty() || !board_list.isSeq() || board_list.size() % 2 != 0 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "%s can not be readed or is not valid\n", (folder + filename).c_str() );
|
||||
ts->printf( cvtest::TS::LOG, "%s can not be read or is not valid\n", (folder + filename).c_str() );
|
||||
ts->printf( cvtest::TS::LOG, "fs.isOpened=%d, board_list.empty=%d, board_list.isSeq=%d,board_list.size()%2=%d\n",
|
||||
fs.isOpened(), (int)board_list.empty(), board_list.isSeq(), board_list.size()%2);
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA );
|
||||
|
@ -85,7 +85,7 @@ void CV_ChessboardDetectorTimingTest::run( int start_from )
|
||||
if( !fs || !board_list || !CV_NODE_IS_SEQ(board_list->tag) ||
|
||||
board_list->data.seq->total % 4 != 0 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "chessboard_timing_list.dat can not be readed or is not valid" );
|
||||
ts->printf( cvtest::TS::LOG, "chessboard_timing_list.dat can not be read or is not valid" );
|
||||
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
|
||||
goto _exit_;
|
||||
}
|
||||
|
@ -1764,7 +1764,7 @@ typedef struct CvString
|
||||
}
|
||||
CvString;
|
||||
|
||||
/** All the keys (names) of elements in the readed file storage
|
||||
/** All the keys (names) of elements in the read file storage
|
||||
are stored in the hash to speed up the lookup operations: */
|
||||
typedef struct CvStringHashNode
|
||||
{
|
||||
|
@ -2779,7 +2779,7 @@ cvGraphAddEdgeByPtr( CvGraph* graph,
|
||||
|
||||
if( start_vtx == end_vtx )
|
||||
CV_Error( start_vtx ? CV_StsBadArg : CV_StsNullPtr,
|
||||
"vertex pointers coinside (or set to NULL)" );
|
||||
"vertex pointers coincide (or set to NULL)" );
|
||||
|
||||
edge = (CvGraphEdge*)cvSetNew( (CvSet*)(graph->edges) );
|
||||
assert( edge->flags >= 0 );
|
||||
|
@ -1063,7 +1063,7 @@ cvReadRawDataSlice( const CvFileStorage* fs, CvSeqReader* reader,
|
||||
CV_Error( CV_StsNullPtr, "Null pointer to reader or destination array" );
|
||||
|
||||
if( !reader->seq && len != 1 )
|
||||
CV_Error( CV_StsBadSize, "The readed sequence is a scalar, thus len must be 1" );
|
||||
CV_Error( CV_StsBadSize, "The read sequence is a scalar, thus len must be 1" );
|
||||
|
||||
fmt_pair_count = icvDecodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
|
||||
size_t step = ::icvCalcStructSize(dt, 0);
|
||||
|
@ -246,7 +246,7 @@ namespace cv { namespace cuda { namespace device
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
// Fot all remaining rows in the median filter, add the values to the the histogram
|
||||
// For all remaining rows in the median filter, add the values to the the histogram
|
||||
for (int j=threadIdx.x; j<cols; j+=blockDim.x){
|
||||
for(int i=initStartRow; i<initStopRow; i++){
|
||||
int pos=::min(i,rows-1);
|
||||
|
@ -342,7 +342,7 @@ void cv::cuda::meanShiftSegmentation(InputArray _src, OutputArray _dst, int sp,
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all graph's edges connecting different components (in asceding order)
|
||||
// Sort all graph's edges connecting different components (in ascending order)
|
||||
std::sort(edges.begin(), edges.end());
|
||||
|
||||
// Exclude small components (starting from the nearest couple)
|
||||
|
@ -48,7 +48,7 @@ namespace opencv_test { namespace {
|
||||
|
||||
namespace
|
||||
{
|
||||
cv::Mat createTransfomMatrix(cv::Size srcSize, double angle)
|
||||
cv::Mat createTransformMatrix(cv::Size srcSize, double angle)
|
||||
{
|
||||
cv::Mat M(2, 3, CV_64FC1);
|
||||
|
||||
@ -80,7 +80,7 @@ PARAM_TEST_CASE(BuildWarpAffineMaps, cv::cuda::DeviceInfo, cv::Size, Inverse)
|
||||
|
||||
CUDA_TEST_P(BuildWarpAffineMaps, Accuracy)
|
||||
{
|
||||
cv::Mat M = createTransfomMatrix(size, CV_PI / 4);
|
||||
cv::Mat M = createTransformMatrix(size, CV_PI / 4);
|
||||
cv::Mat src = randomMat(randomSize(200, 400), CV_8UC1);
|
||||
|
||||
cv::cuda::GpuMat xmap, ymap;
|
||||
@ -207,7 +207,7 @@ PARAM_TEST_CASE(WarpAffine, cv::cuda::DeviceInfo, cv::Size, MatType, Inverse, In
|
||||
CUDA_TEST_P(WarpAffine, Accuracy)
|
||||
{
|
||||
cv::Mat src = randomMat(size, type);
|
||||
cv::Mat M = createTransfomMatrix(size, CV_PI / 3);
|
||||
cv::Mat M = createTransformMatrix(size, CV_PI / 3);
|
||||
int flags = interpolation;
|
||||
if (inverse)
|
||||
flags |= cv::WARP_INVERSE_MAP;
|
||||
@ -257,7 +257,7 @@ CUDA_TEST_P(WarpAffineNPP, Accuracy)
|
||||
cv::Mat src = readImageType("stereobp/aloe-L.png", type);
|
||||
ASSERT_FALSE(src.empty());
|
||||
|
||||
cv::Mat M = createTransfomMatrix(src.size(), CV_PI / 4);
|
||||
cv::Mat M = createTransformMatrix(src.size(), CV_PI / 4);
|
||||
int flags = interpolation;
|
||||
if (inverse)
|
||||
flags |= cv::WARP_INVERSE_MAP;
|
||||
|
@ -48,7 +48,7 @@ namespace opencv_test { namespace {
|
||||
|
||||
namespace
|
||||
{
|
||||
cv::Mat createTransfomMatrix(cv::Size srcSize, double angle)
|
||||
cv::Mat createTransformMatrix(cv::Size srcSize, double angle)
|
||||
{
|
||||
cv::Mat M(3, 3, CV_64FC1);
|
||||
|
||||
@ -81,7 +81,7 @@ PARAM_TEST_CASE(BuildWarpPerspectiveMaps, cv::cuda::DeviceInfo, cv::Size, Invers
|
||||
|
||||
CUDA_TEST_P(BuildWarpPerspectiveMaps, Accuracy)
|
||||
{
|
||||
cv::Mat M = createTransfomMatrix(size, CV_PI / 4);
|
||||
cv::Mat M = createTransformMatrix(size, CV_PI / 4);
|
||||
|
||||
cv::cuda::GpuMat xmap, ymap;
|
||||
cv::cuda::buildWarpPerspectiveMaps(M, inverse, size, xmap, ymap);
|
||||
@ -210,7 +210,7 @@ PARAM_TEST_CASE(WarpPerspective, cv::cuda::DeviceInfo, cv::Size, MatType, Invers
|
||||
CUDA_TEST_P(WarpPerspective, Accuracy)
|
||||
{
|
||||
cv::Mat src = randomMat(size, type);
|
||||
cv::Mat M = createTransfomMatrix(size, CV_PI / 3);
|
||||
cv::Mat M = createTransformMatrix(size, CV_PI / 3);
|
||||
int flags = interpolation;
|
||||
if (inverse)
|
||||
flags |= cv::WARP_INVERSE_MAP;
|
||||
@ -260,7 +260,7 @@ CUDA_TEST_P(WarpPerspectiveNPP, Accuracy)
|
||||
cv::Mat src = readImageType("stereobp/aloe-L.png", type);
|
||||
ASSERT_FALSE(src.empty());
|
||||
|
||||
cv::Mat M = createTransfomMatrix(src.size(), CV_PI / 4);
|
||||
cv::Mat M = createTransformMatrix(src.size(), CV_PI / 4);
|
||||
int flags = interpolation;
|
||||
if (inverse)
|
||||
flags |= cv::WARP_INVERSE_MAP;
|
||||
|
@ -199,7 +199,7 @@ TEST(Resize, Downscale)
|
||||
|
||||
// warpAffine & warpPerspective
|
||||
|
||||
Mat createAffineTransfomMatrix(Size srcSize, float angle, bool perspective)
|
||||
Mat createAffineTransformMatrix(Size srcSize, float angle, bool perspective)
|
||||
{
|
||||
cv::Mat M(perspective ? 3 : 2, 3, CV_32FC1);
|
||||
|
||||
@ -220,7 +220,7 @@ TEST(WarpAffine, Rotation)
|
||||
const Size size = randomSize(100, 400);
|
||||
|
||||
Mat src = randomMat(size, CV_32FC1, 0, 1);
|
||||
Mat M = createAffineTransfomMatrix(size, static_cast<float>(CV_PI / 4), false);
|
||||
Mat M = createAffineTransformMatrix(size, static_cast<float>(CV_PI / 4), false);
|
||||
|
||||
GpuMat_<float> d_src(src);
|
||||
GpuMat_<float> d_M;
|
||||
@ -240,7 +240,7 @@ TEST(WarpPerspective, Rotation)
|
||||
const Size size = randomSize(100, 400);
|
||||
|
||||
Mat src = randomMat(size, CV_32FC1, 0, 1);
|
||||
Mat M = createAffineTransfomMatrix(size, static_cast<float>(CV_PI / 4), true);
|
||||
Mat M = createAffineTransformMatrix(size, static_cast<float>(CV_PI / 4), true);
|
||||
|
||||
GpuMat_<float> d_src(src);
|
||||
GpuMat_<float> d_M;
|
||||
|
@ -131,7 +131,7 @@ my $success_structured;
|
||||
}
|
||||
close $in2 or die "Can't close $filein: $!";
|
||||
}
|
||||
#find next else and interprete it
|
||||
#find next else and interpret it
|
||||
open(my $in3, "<", $filein) or die "Can't open $filein: $!";
|
||||
$i3=1;
|
||||
$ifcount3=0;
|
||||
|
@ -119,7 +119,7 @@ my $is_a_corner;
|
||||
}
|
||||
close $in2 or die "Can't close $filein: $!";
|
||||
}
|
||||
#find next else and interprete it
|
||||
#find next else and interpret it
|
||||
open(my $in3, "<", $filein) or die "Can't open $filein: $!";
|
||||
$i3=1;
|
||||
$ifcount3=0;
|
||||
|
@ -2048,7 +2048,7 @@ public:
|
||||
svmType == NU_SVC ? "NU_SVC" :
|
||||
svmType == ONE_CLASS ? "ONE_CLASS" :
|
||||
svmType == EPS_SVR ? "EPS_SVR" :
|
||||
svmType == NU_SVR ? "NU_SVR" : format("Uknown_%d", svmType);
|
||||
svmType == NU_SVR ? "NU_SVR" : format("Unknown_%d", svmType);
|
||||
String kernel_type_str =
|
||||
kernelType == LINEAR ? "LINEAR" :
|
||||
kernelType == POLY ? "POLY" :
|
||||
|
@ -255,8 +255,8 @@ void HOGDescriptor::computeGradient(const Mat& img, Mat& grad, Mat& qangle,
|
||||
Mat_<float> _lut(1, 256);
|
||||
const float* const lut = &_lut(0,0);
|
||||
#if CV_SSE2
|
||||
const int indeces[] = { 0, 1, 2, 3 };
|
||||
__m128i idx = _mm_loadu_si128((const __m128i*)indeces);
|
||||
const int indices[] = { 0, 1, 2, 3 };
|
||||
__m128i idx = _mm_loadu_si128((const __m128i*)indices);
|
||||
__m128i ifour = _mm_set1_epi32(4);
|
||||
|
||||
float* const _data = &_lut(0, 0);
|
||||
@ -273,8 +273,8 @@ void HOGDescriptor::computeGradient(const Mat& img, Mat& grad, Mat& qangle,
|
||||
idx = _mm_add_epi32(idx, ifour);
|
||||
}
|
||||
#elif CV_NEON
|
||||
const int indeces[] = { 0, 1, 2, 3 };
|
||||
uint32x4_t idx = *(uint32x4_t*)indeces;
|
||||
const int indices[] = { 0, 1, 2, 3 };
|
||||
uint32x4_t idx = *(uint32x4_t*)indices;
|
||||
uint32x4_t ifour = vdupq_n_u32(4);
|
||||
|
||||
float* const _data = &_lut(0, 0);
|
||||
|
@ -9013,7 +9013,7 @@ class NativeArray {
|
||||
|
||||
// Implements Boolean test assertions such as EXPECT_TRUE. expression can be
|
||||
// either a boolean expression or an AssertionResult. text is a textual
|
||||
// represenation of expression as it was passed into the EXPECT_TRUE.
|
||||
// representation of expression as it was passed into the EXPECT_TRUE.
|
||||
#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
|
||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||
if (const ::testing::AssertionResult gtest_ar_ = \
|
||||
|
@ -204,7 +204,7 @@ int main( int argc, char** argv )
|
||||
const char* keys =
|
||||
{
|
||||
"{help h| | show help message}"
|
||||
"{pd | | path of directory contains possitive images}"
|
||||
"{pd | | path of directory contains positive images}"
|
||||
"{nd | | path of directory contains negative images}"
|
||||
"{td | | path of directory contains test images}"
|
||||
"{tv | | test video file name}"
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file introduction_to_pca.cpp
|
||||
* @brief This program demonstrates how to use OpenCV PCA to extract the orienation of an object
|
||||
* @brief This program demonstrates how to use OpenCV PCA to extract the orientation of an object
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
|
@ -26,7 +26,7 @@ static void help(char** argv)
|
||||
"\tESC, q - quit the program\n"
|
||||
"\tr - change order of points to rotate transformation\n"
|
||||
"\tc - delete selected points\n"
|
||||
"\ti - change order of points to invers transformation \n"
|
||||
"\ti - change order of points to inverse transformation \n"
|
||||
"\nUse your mouse to select a point and move it to see transformation changes" << endl;
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ private:
|
||||
//! [ResizeBilinearLayer]
|
||||
|
||||
//
|
||||
// The folowing code is used only to generate tutorials documentation.
|
||||
// The following code is used only to generate tutorials documentation.
|
||||
//
|
||||
|
||||
//! [A custom layer interface]
|
||||
|
@ -1091,7 +1091,7 @@ Style x:Key="SkipBackAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{Static
|
||||
</Style>
|
||||
<Style x:Key="PermissionsAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
|
||||
<Setter Property="AutomationProperties.AutomationId" Value="PermissionsAppBarButton"/>
|
||||
<Setter Property="AutomationProperties.Name" Value="Permisions"/>
|
||||
<Setter Property="AutomationProperties.Name" Value="Permissions"/>
|
||||
<Setter Property="Content" Value=""/>
|
||||
</Style>
|
||||
<Style x:Key="HighlightAppBarButtonStyle" TargetType="ButtonBase" BasedOn="{StaticResource AppBarButtonStyle}">
|
||||
|
Loading…
Reference in New Issue
Block a user