I have made a robotic arm. I need it to capture video and detect blue and red color. And move the blue object to left and red object to right. But the problem is, whenever it is finding a color, it is supposed to send a character '1' (for blue) and '2' for red to the serial port. My problem is,
1. First of all it is taking a lot of time to react.
2. If it detects red. sends '2' to the serial port. But it repeats the action. Even if I change object.
3. Or If it detects red. sends '1' to the serial port. But it repeats the action. Even if I change object.
Color is being detected successfully.
Please someone help me. I've got stuck for a long time.
My Processing Code:
import processing.video.*;
import processing.serial.*;
Serial myPort; // Create object from Serial class
String portName;
Capture video;
color trackColorgreen,trackColorblue,trackColoryellow;
float threshold = 20;
float distThreshold = 75;
ArrayList<Blob> blobgreen = new ArrayList<Blob>();
ArrayList<Blob> blobblue = new ArrayList<Blob>();
ArrayList<Blob> blobyellow = new ArrayList<Blob>();
void findcolorgreen()
{
for (int x = 0; x < video.width; x++ ) {
for (int y = 0; y < video.height; y++ ) {
int loc = x + y * video.width;
// What is current color
color currentColor = video.pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float r2 = red(trackColorgreen);
float g2 = green(trackColorgreen);
float b2 = blue(trackColorgreen);
float d = distSq(r1, g1, b1, r2, g2, b2);
if (d < threshold*threshold) {
boolean found = false;
for (Blob b : blobgreen) {
if (b.isNear(x, y)) {
b.add(x, y);
found = true;
break;
}
}
if (!found) {
Blob b = new Blob(x, y);
blobgreen.add(b);
}
}
}
}
}
void findcolorblue()
{
for (int x = 0; x < video.width; x++ ) {
for (int y = 0; y < video.height; y++ ) {
int loc = x + y * video.width;
// What is current color
color currentColor = video.pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float r2 = red(trackColorblue);
float g2 = green(trackColorblue);
float b2 = blue(trackColorblue);
float d = distSq(r1, g1, b1, r2, g2, b2);
if (d < threshold*threshold) {
boolean found = false;
for (Blob b : blobblue) {
if (b.isNear(x, y)) {
b.add(x, y);
found = true;
break;
}
}
if (!found) {
Blob b = new Blob(x, y);
blobblue.add(b);
}
}
}
}
}
void setup() {
size(640, 360);
String[] cameras = Capture.list();
printArray(cameras);
video = new Capture(this, cameras[3]);
video.start();
//trackColorgreen = color(11,77,28);
trackColorgreen = color(159,211,207);
//trackColorblue = color( 22,135,217);
trackColorblue = color(195,173,175);
//trackColoryellow = color(179,136,9);
// trackColoryellow = color(221,228,125);
portName = Serial.list()[0]; //change the 0 to a 1 or 2 etc. to match your port
myPort = new Serial(this, portName, 9600);
}
void captureEvent(Capture video) {
video.read();
}
void draw() {
video.loadPixels();
image(video, 0, 0);
blobgreen.clear();
blobyellow.clear();
blobblue.clear();
//threshold = map(mouseX, 0, width, 0, 100);
threshold = 80;
// Begin loop to walk through every pixel
findcolorgreen();
findcolorblue();
if(blobgreen.size()==0 && blobblue.size()==0 && blobyellow.size()==0)myPort.write('0');
for (Blob b : blobblue) {
if (b.size() > 800) {
b.show();
myPort.write('1');
delay(2000);
println("blue");
}
}
for (Blob b : blobgreen) {
if (b.size() > 800) {
b.show();
myPort.write('2');
delay(2000);
println("green");
}
}
}
float distSq(float x1, float y1, float x2, float y2) {
float d = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
return d;
}
float distSq(float x1, float y1, float z1, float x2, float y2, float z2) {
float d = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) +(z2-z1)*(z2-z1);
return d;
}
void mousePressed() {
// Save color where the mouse is clicked in trackColor variable
int loc = mouseX + mouseY*video.width;
color trackColor = video.pixels[loc];
}
class Blob {
float minx;
float miny;
float maxx;
float maxy;
Blob(float x, float y) {
minx = x;
miny = y;
maxx = x;
maxy = y;
}
void show() {
stroke(0);
fill(255);
strokeWeight(2);
rectMode(CORNERS);
rect(minx, miny, maxx, maxy);
}
void add(float x, float y) {
minx = min(minx, x);
miny = min(miny, y);
maxx = max(maxx, x);
maxy = max(maxy, y);
}
float size() {
return (maxx-minx)*(maxy-miny);
}
boolean isNear(float x, float y) {
float cx = (minx + maxx) / 2;
float cy = (miny + maxy) / 2;
float d = distSq(cx, cy, x, y);
if (d < distThreshold*distThreshold) {
return true;
} else {
return false;
}
}
}
Arduino Code:
#include <Servo.h>
Servo s2,s3,s4,s5,s6;
int pos;
char val;
void setup() {
s2.attach(3);
s3.attach(4);
s4.attach(5);
s5.attach(6);
s6.attach(7);
s2.write(90);
s3.write(10);
s4.write(100);
s5.write(70);
s6.write(15);
}
void grab()
{
for(pos=15;pos<=85;pos+=1)
{
s6.write(pos);
s5.write(85-pos);
delay(20);
}
delay(50);
// moving s3 forward
for (pos = 10; pos <= 60; pos += 1) {
s3.write(pos);
delay(20);
}
delay(50);
// moving s4 forward
for (pos = 100; pos >= 80; pos -= 1) {
s4.write(pos);
delay(20);
}
delay(20);
// moving s3 forward
for (pos = 60; pos <= 70; pos += 1) {
s3.write(pos);
delay(20);
}
for(pos=85;pos>=15;pos-=1)
{
s6.write(pos);
int x=-(pos-85);
s5.write(x);
delay(20);
}
s5.write(80);
delay(20);
//s3 initial
// moving s3 forward
for (pos = 70; pos >= 10; pos -= 1) {
s3.write(pos);
delay(20);
}
delay(20);
// moving s4 forward
for (pos = 80; pos <= 100; pos += 1) {
s4.write(pos);
delay(20);
}
delay(20);
}
void move_left()
{
for (pos = 90; pos <= 180; pos += 1) {
s2.write(pos);
delay(20);
}
delay(20);
// moving s3 forward
for (pos = 10; pos <= 60; pos += 1) {
s3.write(pos);
delay(20);
}
delay(20);
// moving s4 forward
for (pos = 100; pos >= 70; pos -= 1) {
s4.write(pos);
delay(20);
}
delay(50);
// moving s3 forward
for (pos = 60; pos <= 70; pos += 1) {
s3.write(pos);
delay(20);
}
//expanding claw
for(pos=15;pos<=85;pos+=1)
{
s6.write(pos);
s5.write(85-pos);
delay(20);
}
delay(20);
// moving s4 up
for (pos = 70; pos <=110; pos += 1) {
s4.write(pos);
delay(20);
}
delay(20);
//s3 initial
// moving s3 forward
for (pos = 70; pos >= 10; pos -= 1) {
s3.write(pos);
delay(20);
}
delay(20);
// moving s4 forward
for (pos = 110; pos >= 100; pos -= 1) {
s4.write(pos);
delay(20);
}
delay(20);
//For servo 1 rotation
for (pos = 180; pos >= 90; pos -= 1) {
s2.write(pos);
delay(20);
}
delay(20);
for(pos=85;pos>=15;pos-=1)
{
s6.write(pos);
int x=-(pos-85);
s5.write(x);
delay(20);
}
s5.write(80);
delay(20);
}
void move_right()
{
for (pos = 90; pos >= 0; pos -= 1) {
s2.write(pos);
delay(50);
}
delay(50);
delay(50);
// moving s3 forward
for (pos = 10; pos <= 60; pos += 1) {
s3.write(pos);
delay(50);
}
delay(50);
// moving s4 forward
for (pos = 100; pos >= 70; pos -= 1) {
s4.write(pos);
delay(50);
}
delay(50);
// moving s3 forward
for (pos = 60; pos <= 70; pos += 1) {
s3.write(pos);
delay(50);
}
//expanding claw
for(pos=15;pos<=85;pos+=1)
{
s6.write(pos);
s5.write(85-pos);
delay(20);
}
delay(50);
// moving s4 up
for (pos = 70; pos <=110; pos += 1) {
s4.write(pos);
delay(50);
}
delay(50);
// s3.write(10);
//delay(500);
//s4.write(100);
//s3 initial
// moving s3 forward
for (pos = 70; pos >= 10; pos -= 1) {
s3.write(pos);
delay(50);
}
delay(50);
// moving s4 forward
for (pos = 110; pos >= 100; pos -= 1) {
s4.write(pos);
delay(50);
}
delay(50);
//For servo 1 rotation
for (pos = 0; pos <= 90; pos += 1) {
s2.write(pos);
delay(50);
}
delay(50);
for(pos=85;pos>=15;pos-=1)
{
s6.write(pos);
int x=-(pos-85);
s5.write(x);
delay(20);
}
s5.write(80);
delay(50);
}
void loop() {
if (Serial.available())
{
val = Serial.read(); // read it and store it in val
if(val=='1')
{
grab();
delay(200);
move_left();
delay(200);
}
else if(val=='2')
{
grab();
delay(200);
move_right();
delay(200);
}
}
}
My robot:
https://drive.google.com/open?id=0B9JjLX3LHgUSSU40U3YzOU1LYXc
Related
How can I calculate distance between a fixed parameter and a target image/pixel?
The following code does color recognition, finds the average position, and draws circle on it. It is able to find if the target (averageX and averageY) is close to leftPd, centerPd, or rightPd. I want to change this code as lane tracking which is at least able to find distance value between leftPd parameter variable and left lane or rightPd parameter variable and right lane.
import processing.video.*;
Capture video;
float threshold = 210;
color trackColor;
PVector leftP, centerP, rightP, target;
void setup() {
leftP = new PVector (80,420);
centerP = new PVector (width/2, 380);
rightP = new PVector (560,420);
size(640, 480);
video = new Capture(this, width, height);
video.start();
trackColor = color(160,0,0); // Start off tracking for red
}
void captureEvent(Capture video) {
// Read image from the camera
video.read();
}
void draw() {
loadPixels();
video.loadPixels();
image(video, 0, 0);
float avgX = 0;
float avgY = 0;
int count = 0;
for (int x = 0; x < video.width; x ++ ) {
for (int y = 0; y < video.height; y ++ ) {
int loc = x + y*video.width;
color currentColor = video.pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float r2 = red(trackColor);
float g2 = green(trackColor);
float b2 = blue(trackColor);
// Using euclidean distance to compare colors
float d = distSq(r1, g1, b1, r2, g2, b2);
if (d < threshold) {
stroke(255);
strokeWeight(1);
point(x,y);
avgX += x;
avgY += y;
count++;
}
}
}
if (count > 0) {
avgX = avgX / count;
avgY = avgY / count;
// Draw a circle at the tracked pixel
fill(trackColor);
strokeWeight(4.0);
stroke(0);
ellipse(avgX, avgY, 20, 20);
text("brightnesslevel: " + trackColor, 20, 60);
text("FPS: " + frameRate, 20, 80);
}
target = new PVector (avgX, avgY);
color c = color(255, 204, 0);
fill(c);
noStroke();
ellipse(leftP.x,leftP.y,16,16); // left param
ellipse(centerP.x,centerP.y,16,16); // center param
ellipse(rightP.x,rightP.y,16,16); // right param
float leftPd = leftP.dist(target);
float centerPd = centerP.dist(target);
float rightPd = rightP.dist(target);
if ( leftPd <= 85 ){
text("To Close left " , 20, 250);
}
if ( centerPd <= 85 ){
text("To Close turn center " , 20, 275);
}
if ( rightPd <= 85 ){
text("To Close turn right " , 20, 300);
}
}
float distSq(float x1,float y1, float z1, float x2, float y2, float z2){
float d = (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1);
return d;
}
void mousePressed() {
// Save color where the mouse is clicked in trackColor variable
int loc = mouseX + mouseY*video.width;
trackColor = video.pixels[loc];
}
I need a way to segment each arrow alone. I tried OpenCv findContours but it broke it or add it to multiple shapes and arrows as the share the boundaries of shapes. I tried OpenCV connected components but this arrows almost in some graph connected all of it. Plus having trouble as the boundaries almost have the same color as the arrow. And in these kind of images each arrow contains different colors. Any opinion about this problem.
This is a sample diagram. I have to deal with harder diagrams like this.
Ok, work with new picture.
1. Binarization the arrows (and shapes):
cv::Mat imgCl = cv::imread("62uoU.jpg", cv::IMREAD_COLOR);
cv::Mat img;
cv::cvtColor(imgCl, img, cv::COLOR_BGR2GRAY);
cv::Mat mask1;
cv::threshold(img, mask1, 30, 255, cv::THRESH_BINARY_INV);
cv::Mat mask2;
cv::threshold(img, mask2, 120, 255, cv::THRESH_BINARY_INV);
cv::Mat diff;
cv::absdiff(mask1, mask2, diff);
cv::imshow("diff1", diff);
Result 1:
Remove rectangle shapes:
cv::Rect objRect(0, 0, diff.cols, diff.rows);
cv::Size minSize(objRect.width / 100, objRect.height / 100);
cv::Mat bin = cv::Mat(diff, objRect).clone();
for (;;)
{
cv::Rect cutRect;
if (!PosRefinement(bin, cutRect, 0.9f, minSize))
{
break;
}
cv::rectangle(bin, cutRect, cv::Scalar(0, 0, 0), cv::FILLED);
cv::rectangle(diff, cutRect, cv::Scalar(0, 0, 0), cv::FILLED);
objRect.x += cutRect.x;
objRect.y += cutRect.y;
objRect.width = cutRect.width;
objRect.height = cutRect.height;
}
cv::imshow("diff", diff);
Result 2:
Find lines:
std::vector<cv::Vec4i> linesP;
cv::HoughLinesP(diff, linesP, 1, CV_PI / 180, 20, 10, 5);
for (size_t i = 0; i < linesP.size(); i++)
{
cv::Vec4i l = linesP[i];
cv::line(imgCl, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 3, cv::LINE_AA);
}
cv::imshow("img", imgCl);
Result 3:
Black arrows was founded. It can to improve this solution: find and delete text areas from image (tesseract or cv::text::ERFilter). And add a little morphology for draw arrow tips with Hough lines.
P.S. Utility function:
bool PosRefinement(
cv::Mat bin,
cv::Rect& cutRect,
double kThreshold,
cv::Size minSize
)
{
const double areaThreshold = 100;
const int radius = 5;
const int maxIters = 100;
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(bin, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point());
size_t bestCont = contours.size();
double maxArea = 0;
for (size_t i = 0; i < contours.size(); i++)
{
double area = cv::contourArea(contours[i]);
if (area > maxArea)
{
maxArea = area;
bestCont = i;
}
}
if (maxArea < areaThreshold)
{
return false;
}
cv::Moments m = cv::moments(contours[bestCont]);
cv::Point mc(cvRound(m.m10 / m.m00), cvRound(m.m01 / m.m00));
cv::Rect currRect(mc.x - radius / 2, mc.y - radius / 2, radius, radius);
auto Clamp = [](int v, int hi) -> bool
{
if (v < 0)
{
v = 0;
return true;
}
else if (hi && v > hi - 1)
{
v = hi - 1;
return true;
}
return false;
};
auto RectClamp = [&](cv::Rect& r, int w, int h) -> bool
{
return Clamp(r.x, w) || Clamp(r.x + r.width, w) || Clamp(r.y, h) || Clamp(r.y + r.height, h);
};
int stepL = radius / 2;
int stepR = radius / 2;
int stepT = radius / 2;
int stepB = radius / 2;
double k = 0;
struct State
{
double k = 0;
int stepL = 0;
int stepR = 0;
int stepT = 0;
int stepB = 0;
cv::Rect currRect;
State() = default;
State(double k_, int stepL_, int stepR_, int stepT_, int stepB_, cv::Rect currRect_)
:
k(k_),
stepL(stepL_),
stepR(stepR_),
stepT(stepT_),
stepB(stepB_),
currRect(currRect_)
{
}
bool operator==(const State& st) const
{
return (st.k == k) && (st.stepL == stepL) && (st.stepR == stepR) && (st.stepT == stepT) && (st.stepB == stepB) && (st.currRect == currRect);
}
};
const size_t statesCount = 2;
State prevStates[statesCount];
size_t stateInd = 0;
for (int it = 0; it < maxIters; ++it)
{
cv::Rect rleft(currRect.x - stepL, currRect.y, currRect.width + stepL, currRect.height);
cv::Rect rright(currRect.x, currRect.y, currRect.width + stepR, currRect.height);
cv::Rect rtop(currRect.x, currRect.y - stepT, currRect.width, currRect.height + stepT);
cv::Rect rbottom(currRect.x, currRect.y, currRect.width, currRect.height + stepB);
double kleft = 0;
double kright = 0;
double ktop = 0;
double kbottom = 0;
if (!RectClamp(rleft, bin.cols, bin.rows))
{
cv::Rect rstep(currRect.x - stepL, currRect.y, stepL, currRect.height);
if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2)
{
kleft = cv::sum(bin(rleft))[0] / (255.0 * rleft.area());
}
}
if (!RectClamp(rright, bin.cols, bin.rows))
{
cv::Rect rstep(currRect.x + currRect.width, currRect.y, stepR, currRect.height);
if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2)
{
kright = cv::sum(bin(rright))[0] / (255.0 * rright.area());
}
}
if (!RectClamp(rtop, bin.cols, bin.rows))
{
cv::Rect rstep(currRect.x, currRect.y - stepT, currRect.width, stepT);
if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2)
{
ktop = cv::sum(bin(rtop))[0] / (255.0 * rtop.area());
}
}
if (!RectClamp(rbottom, bin.cols, bin.rows))
{
cv::Rect rstep(currRect.x, currRect.y + currRect.height, currRect.width, stepB);
if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2)
{
kbottom = cv::sum(bin(rbottom))[0] / (255.0 * rbottom.area());
}
}
bool wasEnlargeX = false;
if (kleft > kThreshold)
{
currRect.x -= stepL;
currRect.width += stepL;
wasEnlargeX = true;
if (kleft > k)
{
++stepL;
}
}
else
{
if (stepL > 1)
{
--stepL;
}
currRect.x += 1;
currRect.width -= 1;
}
if (kright > kThreshold)
{
currRect.width += stepR;
wasEnlargeX = true;
if (kright > k)
{
++stepR;
}
}
else
{
if (stepR > 1)
{
--stepR;
}
currRect.width -= 1;
}
bool wasEnlargeY = false;
if (ktop > kThreshold)
{
currRect.y -= stepT;
currRect.height += stepT;
wasEnlargeY = true;
if (ktop > k)
{
++stepT;
}
}
else
{
if (stepT > 1)
{
--stepT;
}
currRect.y += 1;
currRect.height -= 1;
}
if (kbottom > kThreshold)
{
currRect.height += stepB;
wasEnlargeY = true;
if (kbottom > k)
{
++stepB;
}
}
else
{
if (stepB > 1)
{
--stepB;
}
currRect.height -= 1;
}
k = cv::sum(bin(currRect))[0] / (255.0 * currRect.area());
State currState(k, stepL, stepR, stepT, stepB, currRect);
bool repState = false;
for (size_t i = 0; i < statesCount; ++i)
{
if (prevStates[i] == currState)
{
repState = true;
break;
}
}
if (repState)
{
break;
}
else
{
prevStates[stateInd] = currState;
stateInd = (stateInd + 1 < statesCount) ? (stateInd + 1) : 0;
}
if (k < kThreshold && (stepL + stepR + stepT + stepB == 4) && !wasEnlargeX && !wasEnlargeY)
{
break;
}
}
cutRect.x = std::max(0, currRect.x - 1);
cutRect.width = currRect.width + 2;
cutRect.y = std::max(0, currRect.y - 1);
cutRect.height = currRect.height + 2;
return (cutRect.width >= minSize.width) && (cutRect.height >= minSize.height);
}
For your example it might be simple. The picture (png) has 4 channels and 4th channel is transparent mask. It can work only with transparent channel and filter arrows with moments:
cv::Mat img = cv::imread("voXFs.png", cv::IMREAD_UNCHANGED);
std::cout << "imsize = " << img.size() << ", chans = " << img.channels() << std::endl;
cv::imshow("img", img);
std::vector<cv::Mat> chans;
cv::split(img, chans);
cv::imshow("transp", chans.back());
cv::Mat mask;
cv::threshold(chans.back(), mask, 50, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
cv::Mat draw;
cv::cvtColor(mask, draw, cv::COLOR_GRAY2BGR);
for (size_t i = 0; i < contours.size(); ++i)
{
double area = cv::contourArea(contours[i]);
double len = cv::arcLength(contours[i], false);
double k = len / area;
if (area > 10 && len > 60 && k > 2)
{
std::cout << "area = " << area << ", len = " << len << ", k = " << k << std::endl;
cv::drawContours(draw, contours, i, cv::Scalar(255, 0, 0), 1);
}
}
cv::imshow("mask", mask);
cv::imshow("draw", draw);
cv::waitKey(0);
But for more robust result:
Find and delete text areas from image (tesseract or cv::text::ERFilter).
Erode mask, find all shapes by contours, draw and dilate they. Bitwise and operation for mask and result.
The end!
Right now i am using this code to remove the black borders but i am still left with some black part in most of the cases after using this.
Am i doing something wrong in this ?
void cropImage(const vector<cv::Mat>& input, vector<cv::Mat>& output){
// CHECK(!input.empty());
const int imgWidth = input[0].cols;
const int imgHeight = input[0].rows;
Mat cropMask(imgHeight, imgWidth, CV_32F, Scalar::all(0));
for(auto y=0; y<imgHeight; ++y){
for(auto x=0; x<imgWidth; ++x){
bool has_black = false;
for(auto v=0; v<input.size(); ++v){
if(input[v].at<Vec3b>(y,x) == Vec3b(0,0,0)){
has_black = true;
break;
}
}
if(has_black)
cropMask.at<float>(y,x) = -1000;
else
cropMask.at<float>(y,x) = 1;
}
}
Mat integralImage;
cv::integral(cropMask, integralImage, CV_32F);
Vector4i roi;
//int x11=0,x22=0,y11=0,y22=0;
float optValue = -1000 * imgWidth * imgHeight;
const int stride = 20;
for(auto x1=0; x1<imgWidth; x1+=stride) {
for (auto y1 = 0; y1 < imgHeight; y1+=stride) {
for (auto x2 = x1 + stride; x2 < imgWidth; x2+=stride) {
for (auto y2 = y1 + stride; y2 < imgHeight; y2+=stride) {
float curValue = integralImage.at<float>(y2, x2) +
integralImage.at<float>(y1, x1)
- integralImage.at<float>(y2, x1) -
integralImage.at<float>(y1, x2);
if(curValue > optValue){
optValue = curValue;
roi = Vector4i(x1,y1,x2,y2);
}
}
}
}
}
output.resize(input.size());
for(auto i=0; i<output.size(); ++i){
output[i] = input[i].colRange(roi[0],roi[2]).rowRange(roi[1],
roi[3]).clone();
cv::resize(output[i], output[i], cv::Size(imgWidth, imgHeight));
}
}
Also this code seems to be slow. Is there any fast method to achieve the same ? Thanks in advance.
Can anyone help me with this problem, how to do flipping of an image without using the inbuilt flipping function i.e. flip(src image, destination image , 1 or 0) in C++ using OpenCV. I am new to this software so please help.
OpenCV's flip function uses internal flipHoriz or flipVert functions.
static void
flipHoriz( const uchar* src, size_t sstep, uchar* dst, size_t dstep, Size size, size_t esz )
{
int i, j, limit = (int)(((size.width + 1)/2)*esz);
AutoBuffer<int> _tab(size.width*esz);
int* tab = _tab;
for( i = 0; i < size.width; i++ )
for( size_t k = 0; k < esz; k++ )
tab[i*esz + k] = (int)((size.width - i - 1)*esz + k);
for( ; size.height--; src += sstep, dst += dstep )
{
for( i = 0; i < limit; i++ )
{
j = tab[i];
uchar t0 = src[i], t1 = src[j];
dst[i] = t1; dst[j] = t0;
}
}
}
static void
flipVert( const uchar* src0, size_t sstep, uchar* dst0, size_t dstep, Size size, size_t esz )
{
const uchar* src1 = src0 + (size.height - 1)*sstep;
uchar* dst1 = dst0 + (size.height - 1)*dstep;
size.width *= (int)esz;
for( int y = 0; y < (size.height + 1)/2; y++, src0 += sstep, src1 -= sstep,
dst0 += dstep, dst1 -= dstep )
{
int i = 0;
if( ((size_t)src0|(size_t)dst0|(size_t)src1|(size_t)dst1) % sizeof(int) == 0 )
{
for( ; i <= size.width - 16; i += 16 )
{
int t0 = ((int*)(src0 + i))[0];
int t1 = ((int*)(src1 + i))[0];
((int*)(dst0 + i))[0] = t1;
((int*)(dst1 + i))[0] = t0;
t0 = ((int*)(src0 + i))[1];
t1 = ((int*)(src1 + i))[1];
((int*)(dst0 + i))[1] = t1;
((int*)(dst1 + i))[1] = t0;
t0 = ((int*)(src0 + i))[2];
t1 = ((int*)(src1 + i))[2];
((int*)(dst0 + i))[2] = t1;
((int*)(dst1 + i))[2] = t0;
t0 = ((int*)(src0 + i))[3];
t1 = ((int*)(src1 + i))[3];
((int*)(dst0 + i))[3] = t1;
((int*)(dst1 + i))[3] = t0;
}
for( ; i <= size.width - 4; i += 4 )
{
int t0 = ((int*)(src0 + i))[0];
int t1 = ((int*)(src1 + i))[0];
((int*)(dst0 + i))[0] = t1;
((int*)(dst1 + i))[0] = t0;
}
}
for( ; i < size.width; i++ )
{
uchar t0 = src0[i];
uchar t1 = src1[i];
dst0[i] = t1;
dst1[i] = t0;
}
}
}
// you can use it with a small modification as below
void myflip( InputArray _src, OutputArray _dst, int flip_mode )
{
CV_Assert( _src.dims() <= 2 );
Size size = _src.size();
if (flip_mode < 0)
{
if (size.width == 1)
flip_mode = 0;
if (size.height == 1)
flip_mode = 1;
}
if ((size.width == 1 && flip_mode > 0) ||
(size.height == 1 && flip_mode == 0) ||
(size.height == 1 && size.width == 1 && flip_mode < 0))
{
return _src.copyTo(_dst);
}
Mat src = _src.getMat();
int type = src.type();
_dst.create( size, type );
Mat dst = _dst.getMat();
size_t esz = CV_ELEM_SIZE(type);
if( flip_mode <= 0 )
flipVert( src.ptr(), src.step, dst.ptr(), dst.step, src.size(), esz );
else
flipHoriz( src.ptr(), src.step, dst.ptr(), dst.step, src.size(), esz );
if( flip_mode < 0 )
flipHoriz( dst.ptr(), dst.step, dst.ptr(), dst.step, dst.size(), esz );
}
Assuming you have a good reason not to use OpenCV flip function, you can write your custom one.
For this example, I'll use CV_8UC3 images. I'll point out at the end how to expand this to different formats.
Let's see first how to flip an image x axis, which corresponds to cv::flip(src, dst, 1). Given an src image, the dst image will have the same y coordinate, and x coordinate as src.cols - 1 - x coordinates. In practice:
void flip_lr(const Mat3b& src, Mat3b& dst)
{
Mat3b _dst(src.rows, src.cols);
for (int r = 0; r < _dst.rows; ++r) {
for (int c = 0; c < _dst.cols; ++c) {
_dst(r, c) = src(r, src.cols - 1 - c);
}
}
dst = _dst;
}
Then, to flip around y axis (corresponding to cv::flip(src, dst, 0)), dst will have the same x coordinate, and y as src.rows - 1 - y. However, you can reuse the above-mentioned function, simply transposing the dst matrix, apply flip on x axis, and then transpose back. In practice:
dst = src.t();
flip_lr(dst, dst);
dst = dst.t();
Then, to flip both axis, corresponding to cv::flip(src, dst, -1), you need simply to combine the flip on x and y axis:
flip_lr(src, dst);
dst = dst.t();
flip_lr(dst, dst);
dst = dst.t();
You can wrap this functionality in a custom flip function that takes the same parameters as cv::flip:
void custom_flip(const Mat3b& src, Mat3b& dst, int code)
{
if (code > 0)
{ // Flip x axis
flip_lr(src, dst);
}
else if (code == 0)
{
// Flip y axis
dst = src.t();
flip_lr(dst, dst);
dst = dst.t();
}
else // code < 0
{
// Flip x and y axis
flip_lr(src, dst);
dst = dst.t();
flip_lr(dst, dst);
dst = dst.t();
}
}
Note that you can adapt this to different format simply modifing the flip_lr function, and taking care to call the appropriate version inside custom_flip, that will now accept Mat instead of Mat3b.
Full code for reference:
void flip_lr(const Mat3b& src, Mat3b& dst)
{
Mat3b _dst(src.rows, src.cols);
for (int r = 0; r < _dst.rows; ++r) {
for (int c = 0; c < _dst.cols; ++c) {
_dst(r, c) = src(r, src.cols - 1 - c);
}
}
dst = _dst;
}
void custom_flip(const Mat3b& src, Mat3b& dst, int code)
{
if (code > 0)
{ // Flip x axis
flip_lr(src, dst);
}
else if (code == 0)
{
// Flip y axis
dst = src.t();
flip_lr(dst, dst);
dst = dst.t();
}
else // code < 0
{
// Flip x and y axis
flip_lr(src, dst);
dst = dst.t();
flip_lr(dst, dst);
dst = dst.t();
}
}
int main(void)
{
Mat3b img = imread("path_to_image");
Mat3b flipped;
flip(img, flipped, -1);
Mat3b custom;
custom_flip(img, custom, -1);
imshow("OpenCV flip", flipped);
imshow("Custom flip", custom);
waitKey();
return 0;
}
I am new to blackberry.
I am trying to convert normal image into sketch effect.I have code to do that in ANDROID.
I have tried to implement it in Blackberry but unable to got output.Here is android code and my blackberry code.
This is android code-
public class ConvolutionMatrix
{
public static final int SIZE = 3;
public double[][] Matrix;
public double Factor = 1;
public double Offset = 1;
public ConvolutionMatrix(int size) {
Matrix = new double[size][size];
}
public void setAll(double value) {
for (int x = 0; x < SIZE; ++x) {
for (int y = 0; y < SIZE; ++y) {
Matrix[x][y] = value;
}
}
}
public void applyConfig(double[][] config) {
for(int x = 0; x < SIZE; ++x) {
for(int y = 0; y < SIZE; ++y) {
Matrix[x][y] = config[x][y];
}
}
}
public static Bitmap computeConvolution3x3(Bitmap src, ConvolutionMatrix matrix) {
int width = src.getWidth();
int height = src.getHeight();
Bitmap result = Bitmap.createBitmap(width, height, src.getConfig());
int A, R, G, B;
int sumR, sumG, sumB;
int[][] pixels = new int[SIZE][SIZE];
for(int y = 0; y < height - 2; ++y) {
for(int x = 0; x < width - 2; ++x) {
// get pixel matrix
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
pixels[i][j] = src.getPixel(x + i, y + j);
}
}
// get alpha of center pixel
A = Color.alpha(pixels[1][1]);
// init color sum
sumR = sumG = sumB = 0;
// get sum of RGB on matrix
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
sumR += (Color.red(pixels[i][j]) * matrix.Matrix[i][j]);
sumG += (Color.green(pixels[i][j]) * matrix.Matrix[i][j]);
sumB += (Color.blue(pixels[i][j]) * matrix.Matrix[i][j]);
}
}
// get final Red
R = (int)(sumR / matrix.Factor + matrix.Offset);
if(R < 0) { R = 0; }
else if(R > 255) { R = 255; }
// get final Green
G = (int)(sumG / matrix.Factor + matrix.Offset);
if(G < 0) { G = 0; }
else if(G > 255) { G = 255; }
// get final Blue
B = (int)(sumB / matrix.Factor + matrix.Offset);
if(B < 0) { B = 0; }
else if(B > 255) { B = 255; }
// apply new pixel
result.setPixel(x + 1, y + 1, Color.argb(A, R, G, B));
}
}
// final image
return result;
}
}
and following is my Blackberry code that I have tried-
public ConvolutionMatrix(int size) {
Matrix = new double[size][size];
}
public void setAll(double value) {
for (int x = 0; x < SIZE; ++x) {
for (int y = 0; y < SIZE; ++y) {
Matrix[x][y] = value;
}
}
}
public void applyConfig(double[][] config) {
for(int x = 0; x < SIZE; ++x) {
for(int y = 0; y < SIZE; ++y) {
Matrix[x][y] = config[x][y];
}
}
}
public static Bitmap computeConvolution3x3(Bitmap src, ConvolutionMatrix matrix) {
int width = src.getWidth();
int height = src.getHeight();
int A, R, G, B;
int sumR, sumG, sumB;
int[] argb= new int[width*height];
int[][]newargb=new int[width][height];
src.getARGB(argb, 0, width, 0, 0, width, height);
for(int y=0;y<=height;y++)
{
for (int x=0;x<=width;x++)
{
System.out.println(""+x);
System.out.println(""+y);
newargb[y][x]=argb[width*y+x];
}
}
int[][] pixels = new int[SIZE][SIZE];
for(int y = 0; y < height - 2; ++y) {
for(int x = 0; x < width - 2; ++x) {
// get pixel matrix
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
pixels[i][j] = newargb[x + i][ y + j];
}
}
A=pixels[1][1];
sumR = sumG = sumB = 0;
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
A =pixels[i][j] >> 24;
R =pixels[i][j]>> 16 & 0xFF;
G =pixels[i][j] >> 8 & 0xFF;
B =pixels[i][j] & 0xFF;
sumR += (R * matrix.Matrix[i][j]);
sumG += (G * matrix.Matrix[i][j]);
sumB += (B * matrix.Matrix[i][j]);
}
}
// get final Red
R = (int)(sumR / matrix.Factor + matrix.Offset);
if(R < 0) { R = 0; }
else if(R > 255) { R = 255; }
// get final Green
G = (int)(sumG / matrix.Factor + matrix.Offset);
if(G < 0) { G = 0; }
else if(G > 255) { G = 255; }
// get final Blue
B = (int)(sumB / matrix.Factor + matrix.Offset);
if(B < 0) { B = 0; }
else if(B > 255) { B = 255; }
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
pixels[i][j]=(A << 24) | (R << 16) | (G << 8) | B;
newargb[x ][ y]=pixels[i][j];
}
}
}
}
for(int y=0;y<=height;y++)
{
for (int x=0;x<=width;x++)
{
argb[width*y+x]=newargb[y][x];
}
}
src.setARGB(argb, 0, width, 0, 0, width, height);
return src;
}
}
Common Code for both android and Blackberry-
public Bitmap EmbossImage(Bitmap src) {
System.out.println("In Emboss Effect Image method");
double[][] SharpConfig = new double[][] {
{ 0 , -1, 0 },
{ -1, 5, -1 },
{ 0 , -1, 0 }
};
ConvolutionMatrix convMatrix = new ConvolutionMatrix(3);
convMatrix.setAll(0);
convMatrix.applyConfig(SharpConfig);
convMatrix.Factor = 1;
convMatrix.offset=130;
return ConvolutionMatrix.computeConvolution3x3(src, convMatrix);
}
I have found the answer for my own question.I found convolution related code from this site:
android image processing.
See in the comments part on this link page.
This is android code.Just make it compatible it for blackberry by changing Color class methods.