cropper.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // cropper.cpp
  2. // SlamView
  3. //
  4. // Created by Mosong Cheng on 4/30/14.
  5. // Copyright (c) 2014 Mosong Cheng. All rights reserved.
  6. //
  7. #include "cropper.h"
  8. using namespace std;
  9. using namespace cv;
  10. /** find the max ROI in which all pixels are not blank, and
  11. ** then crop the image using ROI **/
  12. Mat Cropper::crop(const cv::Mat &img){
  13. if(img.rows==0 || img.cols==0)return img;
  14. Mat f;
  15. crop(img, f, finalROI);
  16. return f;
  17. }
  18. void Cropper::crop(const cv::Mat &src, cv::Mat &dst, cv::Rect &dstBound)
  19. {
  20. if (src.rows==0 || src.cols==0){
  21. dst = src.clone();
  22. dstBound = cv::Rect(0,0,0,0);
  23. return;
  24. }
  25. // -- Step 0: get mask
  26. getMask(src);
  27. rect = cv::Rect(0, 0, src.cols, src.rows);
  28. // -- Step 1: rough frame
  29. framing();
  30. Mat f = Mat(src, rect).clone();
  31. finalROI = rect;
  32. rect = cv::Rect(0, 0, f.cols, f.rows);
  33. //imwrite(SAT::strCombined(debugPath, "zbw20.jpg"), mask*200);
  34. // -- Step 3: start from the center of the image
  35. cv::Rect roi;
  36. Point2i pt(rect.width/2, rect.height/2);
  37. roi = cv::Rect(pt.x, pt.y, 1, 1);
  38. expandROI(roi);
  39. cout<<"roi = "<<roi<<endl;
  40. f = Mat(f, roi).clone();
  41. finalROI = cv::Rect(finalROI.x + roi.x,
  42. finalROI.y + roi.y,
  43. roi.width,
  44. roi.height);
  45. // -- Step : finally return cropped image
  46. dst.release();
  47. dst = f.clone();
  48. f.release();
  49. dstBound = finalROI;
  50. }
  51. /** build the mask for cropping **/
  52. void Cropper::getMask(const Mat& img) {
  53. mask.release();
  54. mask = Mat::ones(img.rows, img.cols, CV_8U);
  55. uchar *qm = (uchar *)mask.ptr(0,0);
  56. Vec3b *qi = (Vec3b *)img.ptr(0,0);
  57. for(int i1=0; i1<img.total(); i1++){
  58. if(*qi==Vec3b(0,0,0)){
  59. *qm = 0;
  60. }
  61. qm++; qi++;
  62. }
  63. }
  64. /** roughly get the frame of image and mask **/
  65. void Cropper::framing() {
  66. Point2i pt1, pt2, delta;
  67. int x0, x1, y0, y1;
  68. // -- find the upper line
  69. pt1 = Point2i(0,0); pt2 = Point2i(rect.width-1, 0);
  70. delta = Point2i(0,1);
  71. searchContentLine(pt1, pt2, delta);
  72. y0 = pt1.y;
  73. // -- find the lower line
  74. pt1 = Point2i(0, rect.height-1); pt2 = Point2i(rect.width-1, rect.height-1);
  75. delta = Point2i(0,-1);
  76. searchContentLine(pt1, pt2, delta);
  77. y1 = pt1.y;
  78. // -- find the left line
  79. pt1 = Point2i(0,0); pt2 = Point2i(0, rect.height-1 );
  80. delta = Point2i(1,0);
  81. searchContentLine(pt1, pt2, delta);
  82. x0 = pt1.x;
  83. // -- find the right line
  84. pt1 = Point2i(rect.width-1, 0); pt2 = Point2i(rect.width-1, rect.height-1);
  85. delta = Point2i(-1,0);
  86. searchContentLine(pt1, pt2, delta);
  87. x1 = pt1.x;
  88. // -- get the new rect
  89. rect = cv::Rect(x0, y0, x1-x0+1, y1-y0+1);
  90. // -- get the new mask
  91. Mat tmp = Mat(mask, rect).clone();
  92. mask.release();
  93. mask = tmp.clone();
  94. tmp.release();
  95. }
  96. /** expand a roi **/
  97. void Cropper::expandROI(cv::Rect &r) {
  98. bool canExpand = true;
  99. Point2i p1, p2;
  100. cv::Rect rp;
  101. while(canExpand){
  102. canExpand = false;
  103. Point2i stp = (rect.tl() - r.tl());
  104. stp.x /=2; stp.y /=2;
  105. p2 = r.br();
  106. while(abs(stp.x) + abs(stp.y)>=1){
  107. p1 = r.tl() + stp;
  108. rp = cv::Rect(p1, p2);
  109. if(!rectHasBlankPixels(rp)){
  110. r = rp;
  111. canExpand = true;
  112. break;
  113. }
  114. stp.x /= 2; stp.y /=2;
  115. }
  116. stp = (rect.br() - r.br());
  117. stp.x /=2; stp.y /=2;
  118. p1 = r.tl();
  119. while(abs(stp.x) + abs(stp.y)>=1){
  120. p2 = r.br() + stp;
  121. rp = cv::Rect(p1, p2);
  122. if(!rectHasBlankPixels(rp)){
  123. r = rp;
  124. canExpand = true;
  125. break;
  126. }
  127. stp.x /=2; stp.y/=2;
  128. }
  129. }
  130. }
  131. /** determine if a line contains any "blank" piexls.
  132. * This line is either horizontal or vertical **/
  133. bool Cropper::lineHasBlankPixels(const Point2i &st, const Point2i &ed) {
  134. bool verticalLine = (st.x == ed.x);
  135. bool horizontalLine = (st.y == ed.y);
  136. //cout<<"vertical = "<<verticalLine<<"; horizon = "<<horizontalLine<<endl;
  137. if(!verticalLine && !horizontalLine)return true; // this case is not processed
  138. Point2i pt(st);
  139. int kstep = 1;
  140. if(verticalLine){
  141. if (ed.y<st.y) kstep = -1;
  142. for(pt.y=st.y; pt.y<=ed.y; pt.y += kstep){
  143. if (mask.at<uchar>(pt)==0){
  144. return true;
  145. }
  146. }
  147. return false;
  148. }
  149. // -- horizontalLine
  150. if (ed.x < st.x) kstep = -1;
  151. for(pt.x=st.x; pt.x<=ed.x; pt.x += kstep){
  152. if (mask.at<uchar>(pt)==0){
  153. return true;
  154. }
  155. }
  156. return false;
  157. }
  158. /** determine if a rectangle contains "blank" pixels **/
  159. bool Cropper::rectHasBlankPixels(const cv::Rect &roi) {
  160. Point2i tr(roi.x + roi.width - 1, roi.y);
  161. Point2i bl(roi.x, roi.y + roi.height - 1);
  162. Point2i br(roi.x + roi.width - 1, roi.y + roi.height - 1);
  163. if(lineHasBlankPixels(roi.tl(), tr))
  164. return true;
  165. if(lineHasBlankPixels(tr, br))
  166. return true;
  167. if(lineHasBlankPixels(bl, br))
  168. return true;
  169. if(lineHasBlankPixels(roi.tl(), bl))
  170. return true;
  171. return false;
  172. }
  173. /** determine if a line contains "content", i.e, "non-blank" pixels **/
  174. bool Cropper::lineHasContent(const Point2i &st, const Point2i &ed) {
  175. bool verticalLine = (st.x == ed.x);
  176. bool horizontalLine = (st.y == ed.y);
  177. if(!verticalLine && !horizontalLine)return true; // this case is not processed
  178. Point2i pt(st);
  179. int kstep = 1;
  180. if(verticalLine){
  181. if (ed.y<st.y) kstep = -1;
  182. for(pt.y=st.y; pt.y<=ed.y; pt.y += kstep){
  183. if (mask.at<uchar>(pt)>0){
  184. return true;
  185. }
  186. }
  187. return false;
  188. }
  189. // -- horizontalLine
  190. if (ed.x < st.x) kstep = -1;
  191. for(pt.x=st.x; pt.x<=ed.x; pt.x += kstep){
  192. if (mask.at<uchar>(pt)>0){
  193. return true;
  194. }
  195. }
  196. return false;
  197. }
  198. /** search until no more blank line **/
  199. void Cropper::searchContentLine(Point2i &p1, Point2i &p2, Point2i &delta) {
  200. Point2i p3 = p1, p4 = p2;
  201. if(lineHasContent(p1, p2))return; // p1-p2 is the content line
  202. int stp = kstep;
  203. while(stp>=1){
  204. p3 = p1 + delta*stp;
  205. p4 = p2 + delta*stp;
  206. if(!p3.inside(rect)){
  207. stp /= 2; continue;
  208. }
  209. if(lineHasContent(p3, p4)){
  210. stp /= 2; continue;
  211. }
  212. p1 = p3; p2 = p4;
  213. }
  214. p1 = p1 + delta;
  215. p2 = p2 + delta;
  216. }