ধানের শীষ গণনা করা


81

সাদা চালের বিভিন্ন পরিমাণে রান্না করা শস্যের এই 10 টি চিত্র বিবেচনা করুন।
এইগুলি কেবলমাত্র থাম্বনেলস। একটি চিত্র এটি পূর্ণ আকারে দেখতে ক্লিক করুন।

এ: বি: সি: ডি: ই:একজন B ইংরেজী বর্ণমালার দ্বিতীয় অক্ষর সি ডি ই

ফ: জি: এইচ: আমি: জে:এফ জি এইচ আমি জে

শস্য গণনা: A: 3, B: 5, C: 12, D: 25, E: 50, F: 83, G: 120, H:150, I: 151, J: 200

লক্ষ্য করুন...

  • দানাগুলি একে অপরকে স্পর্শ করতে পারে তবে সেগুলি কখনই ওভারল্যাপ করে না।শস্যের বিন্যাস কখনও একের বেশি শস্যের বেশি হয় না।
  • চিত্রগুলির বিভিন্ন মাত্রা রয়েছে তবে ক্যামেরা এবং ব্যাকগ্রাউন্ড স্থিতিশীল থাকায় এগুলির সমস্ততে ধানের স্কেল সামঞ্জস্যপূর্ণ।
  • দানা কখনই সীমার বাইরে যায় না বা চিত্রের সীমানাকে স্পর্শ করে না।
  • পটভূমি সর্বদা হলুদ-সাদা একই ধারাবাহিক ছায়া।
  • ছোট এবং বড় দানা এক এক করে দানা হিসাবে গণনা করা হয়।

এই 5 পয়েন্ট এই ধরণের সমস্ত চিত্রের গ্যারান্টি রয়েছে।

চ্যালেঞ্জ

এমন একটি প্রোগ্রাম লিখুন যা এই জাতীয় চিত্রগুলিতে গ্রহণ করে এবং যথাসম্ভব যথাযথভাবে, ধানের শীষের সংখ্যা গণনা করে।

আপনার প্রোগ্রামটির চিত্রের ফাইলের নাম নেওয়া উচিত এবং এটি গণনা করা শস্যগুলির সংখ্যা মুদ্রণ করে। আপনার প্রোগ্রামটি অবশ্যই কমপক্ষে এই চিত্র ফাইলের ফর্ম্যাটগুলির মধ্যে একটির জন্য কাজ করবে: জেপিইজি, বিটম্যাপ, পিএনজি, জিআইএফ, টিআইএফএফ (এই মুহূর্তে চিত্রগুলি সমস্ত জেপিইজি রয়েছে)।

আপনি ইমেজ প্রসেসিং এবং কম্পিউটার ভিশন লাইব্রেরি ব্যবহার করতে পারেন।

আপনি 10 টি উদাহরণ চিত্রের আউটপুট হার্ডকোড নাও করতে পারেন। আপনার অ্যালগরিদম একই ধরণের সমস্ত চাল-দানা চিত্রগুলির জন্য প্রযোজ্য। একটি শালীন আধুনিক কম্পিউটারে এটি 5 মিনিটেরও কম সময়ে চালাতে সক্ষম হওয়া উচিতচিত্রের ক্ষেত্রটি 2000 * 2000 পিক্সেলের চেয়ে কম এবং 300 ডলারেরও কম ধানের শীষ থাকলে এটি ।

স্কোরিং

10 টি চিত্রের প্রত্যেকটির জন্য আপনার প্রোগ্রামের পূর্বাভাস দেওয়া শস্যের বিয়োগের প্রকৃত সংখ্যার নিরঙ্কুশ মানটি নিন। আপনার স্কোর পেতে এই পরম মানগুলি যোগ করুন Sum সর্বনিম্ন স্কোর জয়। 0 এর স্কোর নিখুঁত।

সম্পর্কের ক্ষেত্রে সর্বাধিক ভোট দেওয়া উত্তর জয়ী হয়। আমি আপনার প্রোগ্রামটির বৈধতা এবং যথার্থতা যাচাই করতে অতিরিক্ত চিত্রগুলিতে পরীক্ষা করতে পারি।


1
অবশ্যই কাউকে বিজ্ঞান-শিখতে চেষ্টা করতে হবে!

দুর্দান্ত প্রতিযোগিতা! :) বিটিডব্লিউ - আমাদের এই চ্যালেঞ্জের শেষ তারিখ সম্পর্কে কিছু বলতে পারে?
সিরিয়াল

1
@ ল্যাম্বিক ডাউন ডাউন টু :)
ডাঃ বেলিসারিয়াস

5
একদিন, একজন ধানের বিজ্ঞানী এই প্রশ্নটি উপস্থিত থাকার কারণে খুশি হয়ে মাথা উঁচু করে চলেছেন।
নিট

2
শুধু তাদের বলুন @Nit ncbi.nlm.nih.gov/pmc/articles/PMC3510117 :)
ডঃ belisarius

উত্তর:


22

গণিত, স্কোর: 7

i = {"http://i.stack.imgur.com/8T6W2.jpg",  "http://i.stack.imgur.com/pgWt1.jpg", 
     "http://i.stack.imgur.com/M0K5w.jpg",  "http://i.stack.imgur.com/eUFNo.jpg", 
     "http://i.stack.imgur.com/2TFdi.jpg",  "http://i.stack.imgur.com/wX48v.jpg", 
     "http://i.stack.imgur.com/eXCGt.jpg",  "http://i.stack.imgur.com/9na4J.jpg",
     "http://i.stack.imgur.com/UMP9V.jpg",  "http://i.stack.imgur.com/nP3Hr.jpg"};

im = Import /@ i;

আমি মনে করি ফাংশনটির নামগুলি যথেষ্ট বর্ণনামূলক:

getSatHSVChannelAndBinarize[i_Image]             := Binarize@ColorSeparate[i, "HSB"][[2]]
removeSmallNoise[i_Image]                        := DeleteSmallComponents[i, 100]
fillSmallHoles[i_Image]                          := Closing[i, 1]
getMorphologicalComponentsAreas[i_Image]         := ComponentMeasurements[i, "Area"][[All, 2]]
roundAreaSizeToGrainCount[areaSize_, grainSize_] := Round[areaSize/grainSize]

সমস্ত ছবি একসাথে প্রক্রিয়াকরণ:

counts = Plus @@@
  (roundAreaSizeToGrainCount[#, 2900] & /@
      (getMorphologicalComponentsAreas@
        fillSmallHoles@
         removeSmallNoise@
          getSatHSVChannelAndBinarize@#) & /@ im)

(* Output {3, 5, 12, 25, 49, 83, 118, 149, 152, 202} *)

স্কোরটি হ'ল:

counts - {3, 5, 12, 25, 50, 83, 120, 150, 151, 200} // Abs // Total
(* 7 *)

এখানে আপনি স্কোর সংবেদনশীলতা রিট ব্যবহৃত শস্য আকার দেখতে পারেন:

গণিত গ্রাফিক্স


2
অনেক পরিষ্কার, ধন্যবাদ!
ক্যালভিনের

এই সঠিক পদ্ধতিটি অজগরটিতে অনুলিপি করা যেতে পারে বা অজগর গ্রন্থাগারগুলি না করতে পারে এমন কোনও বিশেষ ম্যাথমেটিকা ​​এখানে করছে?

@ ল্যাম্বিকের কোনও ধারণা নেই। এখানে অজগর নেই। দুঃখিত। (যাইহোক, আমি জন্য সঠিক একই আলগোরিদিম সন্দেহ EdgeDetect[], DeleteSmallComponents[]এবং Dilation[]অন্যত্র বাস্তবায়িত হয়)
ডঃ belisarius

55

পাইথন, স্কোর: 24 16

এই সমাধানটি ফালকোর মতো, "অগ্রভাগ" ক্ষেত্রটি পরিমাপ এবং গড় শস্য ক্ষেত্র দ্বারা এটি ভাগ করার উপর ভিত্তি করে।

আসলে, এই প্রোগ্রামটি যা সনাক্ত করার চেষ্টা করে তা পটভূমি, পূর্বগ্রাউন্ডের মতো এতটা নয়। ধানের শীষগুলি কখনই চিত্রের সীমানাকে স্পর্শ করে না তা ব্যবহার করে, প্রোগ্রামটি শুরু হয় উপরের-বাম কোণে সাদা বন্যায় white বন্যা ভরাট অ্যালগরিদম সংলগ্ন পিক্সেলগুলিকে আঁকায় যদি তাদের এবং বর্তমান পিক্সেলের উজ্জ্বলতার মধ্যে পার্থক্য একটি নির্দিষ্ট প্রান্তিকের মধ্যে থাকে, এইভাবে পটভূমির রঙের ধীরে ধীরে পরিবর্তনের সাথে সামঞ্জস্য হয়। এই পর্যায়ে শেষে চিত্রটি দেখতে এরকম কিছু দেখাচ্ছে:

চিত্র 1

আপনি দেখতে পাচ্ছেন, এটি ব্যাকগ্রাউন্ড সনাক্তকরণে বেশ ভাল কাজ করে তবে শস্যের মধ্যে "আটকা পড়ে" এমন কোনও অঞ্চল এটি ছেড়ে দেয়। আমরা প্রতিটি পিক্সেলের পটভূমির উজ্জ্বলতা অনুমান করে এবং সমান-বা-উজ্জ্বল পিক্সেলকে থামিয়ে এই অঞ্চলগুলি পরিচালনা করি। এই অনুমানটি এর মতো কাজ করে: বন্যা-পূরণের পর্যায়ে আমরা প্রতিটি সারি এবং প্রতিটি কলামের গড় পটভূমির উজ্জ্বলতা গণনা করি। প্রতিটি পিক্সেলের আনুমানিক পটভূমির উজ্জ্বলতা সেই পিক্সেলের সারি এবং কলামের উজ্জ্বলতার গড়। এটি এরকম কিছু তৈরি করে:

চিত্র ২

সম্পাদনা: পরিশেষে, প্রতিটি অবিচ্ছিন্ন অগ্রভূমির ক্ষেত্রফল (অর্থাৎ অ-সাদা) অঞ্চলটি গড়, প্রাক্কলিত, শস্য ক্ষেত্র দ্বারা বিভক্ত হয়, যা আমাদের উল্লিখিত অঞ্চলে শস্য গণনার একটি অনুমান দেয়। এই পরিমাণের যোগফল ফলাফল। প্রাথমিকভাবে, আমরা পুরো পুরোভূমি অঞ্চলের জন্য একই জিনিসটি করেছি, তবে এই পদ্ধতির আক্ষরিক অর্থে আরও সূক্ষ্ম।


from sys import argv; from PIL import Image

# Init
I = Image.open(argv[1]); W, H = I.size; A = W * H
D = [sum(c) for c in I.getdata()]
Bh = [0] * H; Ch = [0] * H
Bv = [0] * W; Cv = [0] * W

# Flood-fill
Background = 3 * 255 + 1; S = [0]
while S:
    i = S.pop(); c = D[i]
    if c != Background:
        D[i] = Background
        Bh[i / W] += c; Ch[i / W] += 1
        Bv[i % W] += c; Cv[i % W] += 1
        S += [(i + o) % A for o in [1, -1, W, -W] if abs(D[(i + o) % A] - c) < 10]

# Eliminate "trapped" areas
for i in xrange(H): Bh[i] /= float(max(Ch[i], 1))
for i in xrange(W): Bv[i] /= float(max(Cv[i], 1))
for i in xrange(A):
    a = (Bh[i / W] + Bv[i % W]) / 2
    if D[i] >= a: D[i] = Background

# Estimate grain count
Foreground = -1; avg_grain_area = 3038.38; grain_count = 0
for i in xrange(A):
    if Foreground < D[i] < Background:
        S = [i]; area = 0
        while S:
            j = S.pop() % A
            if Foreground < D[j] < Background:
                D[j] = Foreground; area += 1
                S += [j - 1, j + 1, j - W, j + W]
        grain_count += int(round(area / avg_grain_area))

# Output
print grain_count

কোম্যান্ড লাইনের মাধ্যমে ইনপুট ফাইলের নাম নেয়।

ফলাফল

      Actual  Estimate  Abs. Error
A         3         3           0
B         5         5           0
C        12        12           0
D        25        25           0
E        50        48           2
F        83        83           0
G       120       116           4
H       150       145           5
I       151       156           5
J       200       200           0
                        ----------
                Total:         16

একজন B ইংরেজী বর্ণমালার দ্বিতীয় অক্ষর সি ডি ই

এফ জি এইচ আমি জে


2
এটি সত্যিই চতুর সমাধান, দুর্দান্ত কাজ!
ক্রিস সাইরেফাইস

1
কোথায় avg_grain_area = 3038.38;থেকে এসেছে?
njzk2

2
হিসাবে গণনা হয় না hardcoding the result?
njzk2

5
@ njzk2 নং দেওয়া হয়েছে এই নিয়মটি The images have different dimensions but the scale of the rice in all of them is consistent because the camera and background were stationary.কেবল এই নিয়মের প্রতিনিধিত্ব করে। ফলাফল, তবে ইনপুট অনুযায়ী পরিবর্তন হয়। আপনি যদি নিয়ম পরিবর্তন করেন তবে এই মানটি পরিবর্তিত হবে তবে ফলাফলটি একই হবে - ইনপুটটির উপর ভিত্তি করে।
অ্যাডাম ডেভিস

6
আমি গড় এলাকার জিনিস দিয়ে ভাল আছি। শস্যক্ষেত্রের অঞ্চল (প্রায়) চিত্র জুড়ে ধ্রুবক।
ক্যালভিনের শখ

28

পাইথন + ওপেনসিভি: স্কোর 27

অনুভূমিক লাইন স্ক্যানিং

আইডিয়া: একবারে এক সারি চিত্রটি স্ক্যান করুন। প্রতিটি লাইনের জন্য, ধানের শীষের সংখ্যাটি গণনা করুন (পিক্সেলটি সাদা বা বিপরীতে কালো হয়ে গেছে কিনা তা পরীক্ষা করে)। যদি লাইনের জন্য দানাগুলির সংখ্যা বৃদ্ধি হয় (পূর্ববর্তী লাইনের তুলনায়) তবে এর অর্থ আমরা একটি নতুন শস্যের মুখোমুখি হয়েছি। যদি সংখ্যাটি হ্রাস পায় তবে এর অর্থ আমরা একটি শস্য পেরিয়ে এসেছি। এই ক্ষেত্রে, মোট ফলাফলের সাথে +1 যোগ করুন।

এখানে চিত্র বর্ণনা লিখুন

Number in red = rice grains encountered for that line
Number in gray = total amount of grains encountered (what we are looking for)

অ্যালগোরিদম যেভাবে কাজ করে তার কারণে একটি পরিষ্কার, খ / ডাব্লু ইমেজ থাকা জরুরী। প্রচুর শব্দ খারাপ ফলাফল দেয়। প্রথম প্রধান ব্যাকগ্রাউন্ডটি ফ্লাডফিল ব্যবহার করে পরিষ্কার করা হয় (ইলের উত্তরের অনুরূপ সমাধান) তারপরে কালো এবং সাদা ফলাফলের জন্য প্রান্তিক প্রয়োগ করা হয়।

এখানে চিত্র বর্ণনা লিখুন

এটি নিখুঁত থেকে দূরে, তবে এটি সরলতার বিষয়ে ভাল ফলাফল দেয়। এটি উন্নত করার জন্য সম্ভবত অনেকগুলি উপায় রয়েছে (আরও ভাল বি / ডব্লু চিত্র সরবরাহ করে, অন্যান্য দিকগুলিতে স্ক্যান করে (উদাহরণস্বরূপ: উল্লম্ব, তির্যক) গড় ইত্যাদি গ্রহণ করে ...)

import cv2
import numpy
import sys

filename = sys.argv[1]
I = cv2.imread(filename, 0)
h,w = I.shape[:2]
diff = (3,3,3)
mask = numpy.zeros((h+2,w+2),numpy.uint8)
cv2.floodFill(I,mask,(0,0), (255,255,255),diff,diff)
T,I = cv2.threshold(I,180,255,cv2.THRESH_BINARY)
I = cv2.medianBlur(I, 7)

totalrice = 0
oldlinecount = 0
for y in range(0, h):
    oldc = 0
    linecount = 0
    start = 0   
    for x in range(0, w):
        c = I[y,x] < 128;
        if c == 1 and oldc == 0:
            start = x
        if c == 0 and oldc == 1 and (x - start) > 10:
            linecount += 1
        oldc = c
    if oldlinecount != linecount:
        if linecount < oldlinecount:
            totalrice += oldlinecount - linecount
        oldlinecount = linecount
print totalrice

চিত্র প্রতি ত্রুটি: 0, 0, 0, 3, 0, 12, 4, 0, 7, 1


24

পাইথন + ওপেনসিভি: স্কোর 84

এখানে প্রথম নির্বোধ চেষ্টা। এটি ম্যানুয়ালি টিউন করা প্যারামিটারগুলির সাথে একটি অভিযোজিত প্রান্তিক প্রয়োগ করে, পরবর্তী ক্ষয় এবং পাতলা করার সাথে কিছু গর্ত বন্ধ করে দেয় এবং অগ্রভাগ অঞ্চল থেকে শস্যের সংখ্যা অর্জন করে।

import cv2
import numpy as np

filename = raw_input()

I = cv2.imread(filename, 0)
I = cv2.medianBlur(I, 3)
bw = cv2.adaptiveThreshold(I, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 101, 1)

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (17, 17))
bw = cv2.dilate(cv2.erode(bw, kernel), kernel)

print np.round_(np.sum(bw == 0) / 3015.0)

এখানে আপনি মধ্যবর্তী বাইনারি চিত্রগুলি দেখতে পাচ্ছেন (কালো রঙের অগ্রভাগ):

এখানে চিত্র বর্ণনা লিখুন

চিত্র প্রতি ত্রুটি 0, 0, 2, 2, 4, 0, 27, 42, 0 এবং 7 দানা হয়।


20

সি # + ওপেনসিভিশার্প, স্কোর: 2

এটি আমার দ্বিতীয় প্রচেষ্টা। এটি আমার প্রথম প্রয়াস থেকে একেবারেই আলাদা , যা অনেক সহজ, তাই আমি এটি একটি পৃথক সমাধান হিসাবে পোস্ট করছি।

মূল ধারণাটি হ'ল একটি পৃথক উপবৃত্তাকার ফিট দ্বারা প্রতিটি পৃথক শস্য চিহ্নিত করা এবং লেবেল করা। তারপরে উত্স থেকে এই শস্যের জন্য পিক্সেলগুলি সরিয়ে ফেলুন এবং প্রতিটি পিক্সেলকে লেবেল না করা পর্যন্ত পরবর্তী শস্যটি অনুসন্ধান করার চেষ্টা করুন।

এটি সবচেয়ে সুন্দর সমাধান নয়। এটি 600০০ লাইনের কোড সহ একটি দৈত্য হোগ। এটি সবচেয়ে বড় চিত্রটির জন্য 1.5 মিনিটের প্রয়োজন। আমি অগোছালো কোডের জন্য সত্যই ক্ষমা চাইছি।

এই জিনিসটিতে অনেক চিন্তাভাবনা করার প্যারামিটার এবং উপায় রয়েছে যে আমি 10 টি নমুনা চিত্রের জন্য আমার প্রোগ্রামকে ফিট করে তুলতে বেশ ভয় পাই। 2 এর চূড়ান্ত স্কোর প্রায় স্পষ্টভাবে overfitting একটি ক্ষেত্রে দেখা যায়: আমি দুটি প্যারামিটার আছে average grain size in pixel, এবং minimum ratio of pixel / elipse_area, এবং শেষে আমি কেবল এই দুটি প্যারামিটার সব সমন্বয় ক্লান্ত না হওয়া পর্যন্ত আমি সর্বনিম্ন স্কোর করেছেন। আমি নিশ্চিত নই যে এই চ্যালেঞ্জের নিয়মগুলির সাথে এই সমস্ত কোশার কিনা।

average_grain_size_in_pixel = 2530
pixel / elipse_area >= 0.73

তবে এই অত্যধিক কৌতুক ছাড়াও ফলাফলগুলি বেশ দুর্দান্ত। একটি নির্দিষ্ট শস্যের আকার বা পিক্সেল-অনুপাত ব্যতীত প্রশিক্ষণের চিত্রগুলি থেকে গড় শস্যের আকার অনুমান করে স্কোরটি এখনও 27 টি।

এবং আমি আউটপুট হিসাবে কেবল সংখ্যাটিই পাই না, তবে প্রতিটি শস্যের আসল অবস্থান, ওরিয়েন্টেশন এবং আকার। এখানে অল্প সংখ্যক বিভ্রান্তিযুক্ত শস্য রয়েছে তবে সামগ্রিকভাবে বেশিরভাগ লেবেলই আসল শস্যের সাথে নির্ভুলভাবে মেলে:

একজন বি B ইংরেজী বর্ণমালার দ্বিতীয় অক্ষর সি সি ডি ডিই

এফ এফ জি জি এইচ এইচ আই আমি জেজে

(পূর্ণ আকারের সংস্করণের জন্য প্রতিটি ছবিতে ক্লিক করুন)

এই লেবেলিং পদক্ষেপের পরে, আমার প্রোগ্রাম প্রতিটি পৃথক শস্য এবং পিক্সেলের সংখ্যার উপর ভিত্তি করে এবং পিক্সেল / উপবৃত্ত-অঞ্চল অনুপাতের ভিত্তিতে অনুমানগুলি দেখায়, এটি কিনা

  • একটি একক শস্য (+1)
  • একাধিক শস্যকে এক (+ এক্স) হিসাবে বিভক্ত করা হয়েছে
  • একটি দানা হতে খুব ছোট একটি ব্লব (+0)

প্রতিটি চিত্রের জন্য ত্রুটি স্কোরগুলি A:0; B:0; C:0; D:0; E:2; F:0; G:0 ; H:0; I:0, J:0

তবে আসল ত্রুটি সম্ভবত কিছুটা বেশি। একই চিত্রের মধ্যে কিছু ত্রুটি একে অপরকে বাতিল করে দেয়। ইমেজ এইচটিতে লেবেলগুলি বেশিরভাগ ক্ষেত্রেই সঠিকভাবে ইমেজ এইচটিতে কিছু খারাপভাবে বিভ্রান্তিকর শস্য রয়েছে

ধারণাটি কিছুটা স্বীকৃত:

  • প্রথমে অগ্রভাগটি স্যাচুরেশন চ্যানেলে ওটসু-থ্রেশহোল্ডিংয়ের মাধ্যমে পৃথক করা হয়েছে (বিশদগুলির জন্য আমার পূর্ববর্তী উত্তরটি দেখুন)

  • আর কোনও পিক্সেল না দেওয়া পর্যন্ত পুনরাবৃত্তি করুন:

    • বৃহত্তম অঙ্কুর নির্বাচন করুন
    • শস্যের জন্য শুরুর অবস্থান হিসাবে এই ফোটাতে 10 টি এলোমেলো প্রান্ত পিক্সেল চয়ন করুন

    • প্রতিটি সূচনা পয়েন্ট জন্য

      • এই অবস্থানে উচ্চতা এবং প্রস্থ 10 পিক্সেল সহ একটি শস্য গ্রহণ করুন।

      • একীকরণ পর্যন্ত পুনরাবৃত্তি

        • আপনি একটি প্রান্ত পিক্সেল (সাদা থেকে কালো) সম্মুখীন না হওয়া অবধি, এই বিন্দু থেকে বিভিন্ন কোণে সূক্ষ্মভাবে বাহিরে যান

        • প্রাপ্ত পিক্সেলগুলি আশা করা উচিত একটি একক শস্যের প্রান্ত পিক্সেল। অন্যের তুলনায় অনুমান উপবৃত্ত থেকে বেশি দূরে যে পিক্সেলগুলি বিচ্ছিন্ন করে আউটলিয়ারদের থেকে ইনিলিয়ারদের আলাদা করার চেষ্টা করুন

        • ইনলিয়ার্সের একটি উপসেটের মাধ্যমে বারবার একটি উপবৃত্তের সাথে ফিট করার চেষ্টা করুন, সেরা উপবৃত্তটি রাখুন (র্যানস্যাক)

        • প্রাপ্ত এলিপ্সের সাথে শস্যের অবস্থান, ওরিয়েন্টেশন, প্রস্থ এবং উচ্চতা আপডেট করুন

        • যদি শস্যের অবস্থান উল্লেখযোগ্যভাবে পরিবর্তন না হয় তবে থামুন

    • 10 টি লাগানো শস্যগুলির মধ্যে, আকার, প্রান্ত পিক্সেলের সংখ্যা অনুযায়ী সেরা শস্য চয়ন করুন। অন্যদের ত্যাগ করুন

    • উত্স চিত্র থেকে এই শস্যের জন্য সমস্ত পিক্সেল সরান, তারপরে পুনরাবৃত্তি করুন

    • অবশেষে, পাওয়া শস্যগুলির তালিকাটি দেখুন এবং প্রতিটি শস্যকে 1 শস্য, 0 দানা (খুব ছোট) বা 2 টি দানা (খুব বড়) হিসাবে গণনা করুন

আমার প্রধান সমস্যাগুলির মধ্যে একটি হ'ল আমি একটি সম্পূর্ণ উপবৃত্ত-পয়েন্ট দূরত্বের মেট্রিক বাস্তবায়ন করতে চাইনি, যেহেতু এটি গণনা করা নিজেই একটি জটিল পুনরাবৃত্তি প্রক্রিয়া। সুতরাং আমি ওপেনসিভি ফাংশন এলিপস 2 পলি এবং ফিটঅ্যালিপস ব্যবহার করে বিভিন্ন কাজের সীমা ব্যবহার করেছি এবং ফলাফলগুলি খুব সুন্দর নয়।

স্পষ্টতই আমি কোডগল্ফের আকারের সীমাটিও ভেঙে দিয়েছি।

একটি উত্তর 30000 অক্ষরের মধ্যে সীমাবদ্ধ, আমি বর্তমানে 34000 এ আছি So সুতরাং আমাকে কিছুটা নীচে কোডটি ছোট করতে হবে।

সম্পূর্ণ কোডটি http://pastebin.com/RgM7hMxq এ দেখা যাবে

এর জন্য দুঃখিত, আমি জানতাম না যে একটি আকার সীমা ছিল।

class Program
{
    static void Main(string[] args)
    {

                // Due to size constraints, I removed the inital part of my program that does background separation. For the full source, check the link, or see my previous program.


                // list of recognized grains
                List<Grain> grains = new List<Grain>();

                Random rand = new Random(4); // determined by fair dice throw, guaranteed to be random

                // repeat until we have found all grains (to a maximum of 10000)
                for (int numIterations = 0; numIterations < 10000; numIterations++ )
                {
                    // erode the image of the remaining foreground pixels, only big blobs can be grains
                    foreground.Erode(erodedForeground,null,7);

                    // pick a number of starting points to fit grains
                    List<CvPoint> startPoints = new List<CvPoint>();
                    using (CvMemStorage storage = new CvMemStorage())
                    using (CvContourScanner scanner = new CvContourScanner(erodedForeground, storage, CvContour.SizeOf, ContourRetrieval.List, ContourChain.ApproxNone))
                    {
                        if (!scanner.Any()) break; // no grains left, finished!

                        // search for grains within the biggest blob first (this is arbitrary)
                        var biggestBlob = scanner.OrderByDescending(c => c.Count()).First();

                        // pick 10 random edge pixels
                        for (int i = 0; i < 10; i++)
                        {
                            startPoints.Add(biggestBlob.ElementAt(rand.Next(biggestBlob.Count())).Value);
                        }
                    }

                    // for each starting point, try to fit a grain there
                    ConcurrentBag<Grain> candidates = new ConcurrentBag<Grain>();
                    Parallel.ForEach(startPoints, point =>
                    {
                        Grain candidate = new Grain(point);
                        candidate.Fit(foreground);
                        candidates.Add(candidate);
                    });

                    Grain grain = candidates
                        .OrderByDescending(g=>g.Converged) // we don't want grains where the iterative fit did not finish
                        .ThenBy(g=>g.IsTooSmall) // we don't want tiny grains
                        .ThenByDescending(g => g.CircumferenceRatio) // we want grains that have many edge pixels close to the fitted elipse
                        .ThenBy(g => g.MeanSquaredError)
                        .First(); // we only want the best fit among the 10 candidates

                    // count the number of foreground pixels this grain has
                    grain.CountPixel(foreground);

                    // remove the grain from the foreground
                    grain.Draw(foreground,CvColor.Black);

                    // add the grain to the colection fo found grains
                    grains.Add(grain);
                    grain.Index = grains.Count;

                    // draw the grain for visualisation
                    grain.Draw(display, CvColor.Random());
                    grain.DrawContour(display, CvColor.Random());
                    grain.DrawEllipse(display, CvColor.Random());

                    //display.SaveImage("10-foundGrains.png");
                }

                // throw away really bad grains
                grains = grains.Where(g => g.PixelRatio >= 0.73).ToList();

                // estimate the average grain size, ignoring outliers
                double avgGrainSize =
                    grains.OrderBy(g => g.NumPixel).Skip(grains.Count/10).Take(grains.Count*9/10).Average(g => g.NumPixel);

                //ignore the estimated grain size, use a fixed size
                avgGrainSize = 2530;

                // count the number of grains, using the average grain size
                double numGrains = grains.Sum(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize));

                // get some statistics
                double avgWidth = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) == 1).Average(g => g.Width);
                double avgHeight = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) == 1).Average(g => g.Height);
                double avgPixelRatio = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) == 1).Average(g => g.PixelRatio);

                int numUndersized = grains.Count(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) < 1);
                int numOversized = grains.Count(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) > 1);

                double avgWidthUndersized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) < 1).Select(g=>g.Width).DefaultIfEmpty(0).Average();
                double avgHeightUndersized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) < 1).Select(g => g.Height).DefaultIfEmpty(0).Average();
                double avgGrainSizeUndersized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) < 1).Select(g => g.NumPixel).DefaultIfEmpty(0).Average();
                double avgPixelRatioUndersized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) < 1).Select(g => g.PixelRatio).DefaultIfEmpty(0).Average();

                double avgWidthOversized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) > 1).Select(g => g.Width).DefaultIfEmpty(0).Average();
                double avgHeightOversized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) > 1).Select(g => g.Height).DefaultIfEmpty(0).Average();
                double avgGrainSizeOversized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) > 1).Select(g => g.NumPixel).DefaultIfEmpty(0).Average();
                double avgPixelRatioOversized = grains.Where(g => Math.Round(g.NumPixel * 1.0 / avgGrainSize) > 1).Select(g => g.PixelRatio).DefaultIfEmpty(0).Average();


                Console.WriteLine("===============================");
                Console.WriteLine("Grains: {0}|{1:0.} of {2} (e{3}), size {4:0.}px, {5:0.}x{6:0.}  {7:0.000}  undersized:{8}  oversized:{9}   {10:0.0} minutes  {11:0.0} s per grain",grains.Count,numGrains,expectedGrains[fileNo],expectedGrains[fileNo]-numGrains,avgGrainSize,avgWidth,avgHeight, avgPixelRatio,numUndersized,numOversized,watch.Elapsed.TotalMinutes, watch.Elapsed.TotalSeconds/grains.Count);



                // draw the description for each grain
                foreach (Grain grain in grains)
                {
                    grain.DrawText(avgGrainSize, display, CvColor.Black);
                }

                display.SaveImage("10-foundGrains.png");
                display.SaveImage("X-" + file + "-foundgrains.png");
            }
        }
    }
}



public class Grain
{
    private const int MIN_WIDTH = 70;
    private const int MAX_WIDTH = 130;
    private const int MIN_HEIGHT = 20;
    private const int MAX_HEIGHT = 35;

    private static CvFont font01 = new CvFont(FontFace.HersheyPlain, 0.5, 1);
    private Random random = new Random(4); // determined by fair dice throw; guaranteed to be random


    /// <summary> center of grain </summary>
    public CvPoint2D32f Position { get; private set; }
    /// <summary> Width of grain (always bigger than height)</summary>
    public float Width { get; private set; }
    /// <summary> Height of grain (always smaller than width)</summary>
    public float Height { get; private set; }

    public float MinorRadius { get { return this.Height / 2; } }
    public float MajorRadius { get { return this.Width / 2; } }
    public double Angle { get; private set; }
    public double AngleRad { get { return this.Angle * Math.PI / 180; } }

    public int Index { get; set; }
    public bool Converged { get; private set; }
    public int NumIterations { get; private set; }
    public double CircumferenceRatio { get; private set; }
    public int NumPixel { get; private set; }
    public List<EllipsePoint> EdgePoints { get; private set; }
    public double MeanSquaredError { get; private set; }
    public double PixelRatio { get { return this.NumPixel / (Math.PI * this.MajorRadius * this.MinorRadius); } }
    public bool IsTooSmall { get { return this.Width < MIN_WIDTH || this.Height < MIN_HEIGHT; } }

    public Grain(CvPoint2D32f position)
    {
        this.Position = position;
        this.Angle = 0;
        this.Width = 10;
        this.Height = 10;
        this.MeanSquaredError = double.MaxValue;
    }

    /// <summary>  fit a single rice grain of elipsoid shape </summary>
    public void Fit(CvMat img)
    {
        // distance between the sampled points on the elipse circumference in degree
        int angularResolution = 1;

        // how many times did the fitted ellipse not change significantly?
        int numConverged = 0;

        // number of iterations for this fit
        int numIterations;

        // repeat until the fitted ellipse does not change anymore, or the maximum number of iterations is reached
        for (numIterations = 0; numIterations < 100 && !this.Converged; numIterations++)
        {
            // points on an ideal ellipse
            CvPoint[] points;
            Cv.Ellipse2Poly(this.Position, new CvSize2D32f(MajorRadius, MinorRadius), Convert.ToInt32(this.Angle), 0, 359, out points,
                            angularResolution);

            // points on the edge of foregroudn to background, that are close to the elipse
            CvPoint?[] edgePoints = new CvPoint?[points.Length];

            // remeber if the previous pixel in a given direction was foreground or background
            bool[] prevPixelWasForeground = new bool[points.Length];

            // when the first edge pixel is found, this value is updated
            double firstEdgePixelOffset = 200;

            // from the center of the elipse towards the outside:
            for (float offset = -this.MajorRadius + 1; offset < firstEdgePixelOffset + 20; offset++)
            {
                // draw an ellipse with the given offset
                Cv.Ellipse2Poly(this.Position, new CvSize2D32f(MajorRadius + offset, MinorRadius + (offset > 0 ? offset : MinorRadius / MajorRadius * offset)), Convert.ToInt32(this.Angle), 0,
                                359, out points, angularResolution);

                // for each angle
                Parallel.For(0, points.Length, i =>
                {
                    if (edgePoints[i].HasValue) return; // edge for this angle already found

                    // check if the current pixel is foreground
                    bool foreground = points[i].X < 0 || points[i].Y < 0 || points[i].X >= img.Cols || points[i].Y >= img.Rows
                                          ? false // pixel outside of image borders is always background
                                          : img.Get2D(points[i].Y, points[i].X).Val0 > 0;


                    if (prevPixelWasForeground[i] && !foreground)
                    {
                        // found edge pixel!
                        edgePoints[i] = points[i];

                        // if this is the first edge pixel we found, remember its offset. the other pixels cannot be too far away, so we can stop searching soon
                        if (offset < firstEdgePixelOffset && offset > 0) firstEdgePixelOffset = offset;
                    }

                    prevPixelWasForeground[i] = foreground;
                });
            }

            // estimate the distance of each found edge pixel from the ideal elipse
            // this is a hack, since the actual equations for estimating point-ellipse distnaces are complicated
            Cv.Ellipse2Poly(this.Position, new CvSize2D32f(MajorRadius, MinorRadius), Convert.ToInt32(this.Angle), 0, 360,
                            out points, angularResolution);
            var pointswithDistance =
                edgePoints.Select((p, i) => p.HasValue ? new EllipsePoint(p.Value, points[i], this.Position) : null)
                          .Where(p => p != null).ToList();

            if (pointswithDistance.Count == 0)
            {
                Console.WriteLine("no points found! should never happen! ");
                break;
            }

            // throw away all outliers that are too far outside the current ellipse
            double medianSignedDistance = pointswithDistance.OrderBy(p => p.SignedDistance).ElementAt(pointswithDistance.Count / 2).SignedDistance;
            var goodPoints = pointswithDistance.Where(p => p.SignedDistance < medianSignedDistance + 15).ToList();

            // do a sort of ransack fit with the inlier points to find a new better ellipse
            CvBox2D bestfit = ellipseRansack(goodPoints);

            // check if the fit has converged
            if (Math.Abs(this.Angle - bestfit.Angle) < 3 && // angle has not changed much (<3°)
                Math.Abs(this.Position.X - bestfit.Center.X) < 3 && // position has not changed much (<3 pixel)
                Math.Abs(this.Position.Y - bestfit.Center.Y) < 3)
            {
                numConverged++;
            }
            else
            {
                numConverged = 0;
            }

            if (numConverged > 2)
            {
                this.Converged = true;
            }

            //Console.WriteLine("Iteration {0}, delta {1:0.000} {2:0.000} {3:0.000}    {4:0.000}-{5:0.000} {6:0.000}-{7:0.000} {8:0.000}-{9:0.000}",
            //  numIterations, Math.Abs(this.Angle - bestfit.Angle), Math.Abs(this.Position.X - bestfit.Center.X), Math.Abs(this.Position.Y - bestfit.Center.Y), this.Angle, bestfit.Angle, this.Position.X, bestfit.Center.X, this.Position.Y, bestfit.Center.Y);

            double msr = goodPoints.Sum(p => p.Distance * p.Distance) / goodPoints.Count;

            // for drawing the polygon, filter the edge points more strongly
            if (goodPoints.Count(p => p.SignedDistance < 5) > goodPoints.Count / 2)
                goodPoints = goodPoints.Where(p => p.SignedDistance < 5).ToList();
            double cutoff = goodPoints.Select(p => p.Distance).OrderBy(d => d).ElementAt(goodPoints.Count * 9 / 10);
            goodPoints = goodPoints.Where(p => p.SignedDistance <= cutoff + 1).ToList();

            int numCertainEdgePoints = goodPoints.Count(p => p.SignedDistance > -2);
            this.CircumferenceRatio = numCertainEdgePoints * 1.0 / points.Count();

            this.Angle = bestfit.Angle;
            this.Position = bestfit.Center;
            this.Width = bestfit.Size.Width;
            this.Height = bestfit.Size.Height;
            this.EdgePoints = goodPoints;
            this.MeanSquaredError = msr;

        }
        this.NumIterations = numIterations;
        //Console.WriteLine("Grain found after {0,3} iterations, size={1,3:0.}x{2,3:0.}   pixel={3,5}    edgePoints={4,3}   msr={5,2:0.00000}", numIterations, this.Width,
        //                        this.Height, this.NumPixel, this.EdgePoints.Count, this.MeanSquaredError);
    }

    /// <summary> a sort of ransakc fit to find the best ellipse for the given points </summary>
    private CvBox2D ellipseRansack(List<EllipsePoint> points)
    {
        using (CvMemStorage storage = new CvMemStorage(0))
        {
            // calculate minimum bounding rectangle
            CvSeq<CvPoint> fullPointSeq = CvSeq<CvPoint>.FromArray(points.Select(p => p.Point), SeqType.EltypePoint, storage);
            var boundingRect = fullPointSeq.MinAreaRect2();

            // the initial candidate is the previously found ellipse
            CvBox2D bestEllipse = new CvBox2D(this.Position, new CvSize2D32f(this.Width, this.Height), (float)this.Angle);
            double bestError = calculateEllipseError(points, bestEllipse);

            Queue<EllipsePoint> permutation = new Queue<EllipsePoint>();
            if (points.Count >= 5) for (int i = -2; i < 20; i++)
                {
                    CvBox2D ellipse;
                    if (i == -2)
                    {
                        // first, try the ellipse described by the boundingg rect
                        ellipse = boundingRect;
                    }
                    else if (i == -1)
                    {
                        // then, try the best-fit ellipsethrough all points
                        ellipse = fullPointSeq.FitEllipse2();
                    }
                    else
                    {
                        // then, repeatedly fit an ellipse through a random sample of points

                        // pick some random points
                        if (permutation.Count < 5) permutation = new Queue<EllipsePoint>(permutation.Concat(points.OrderBy(p => random.Next())));
                        CvSeq<CvPoint> pointSeq = CvSeq<CvPoint>.FromArray(permutation.Take(10).Select(p => p.Point), SeqType.EltypePoint, storage);
                        for (int j = 0; j < pointSeq.Count(); j++) permutation.Dequeue();

                        // fit an ellipse through these points
                        ellipse = pointSeq.FitEllipse2();
                    }

                    // assure that the width is greater than the height
                    ellipse = NormalizeEllipse(ellipse);

                    // if the ellipse is too big for agrain, shrink it
                    ellipse = rightSize(ellipse, points.Where(p => isOnEllipse(p.Point, ellipse, 10, 10)).ToList());

                    // sometimes the ellipse given by FitEllipse2 is totally off
                    if (boundingRect.Center.DistanceTo(ellipse.Center) > Math.Max(boundingRect.Size.Width, boundingRect.Size.Height) * 2)
                    {
                        // ignore this bad fit
                        continue;
                    }

                    // estimate the error
                    double error = calculateEllipseError(points, ellipse);

                    if (error < bestError)
                    {
                        // found a better ellipse!
                        bestError = error;
                        bestEllipse = ellipse;
                    }
                }

            return bestEllipse;
        }
    }

    /// <summary> The proper thing to do would be to use the actual distance of each point to the elipse.
    /// However that formula is complicated, so ...  </summary>
    private double calculateEllipseError(List<EllipsePoint> points, CvBox2D ellipse)
    {
        const double toleranceInner = 5;
        const double toleranceOuter = 10;
        int numWrongPoints = points.Count(p => !isOnEllipse(p.Point, ellipse, toleranceInner, toleranceOuter));
        double ratioWrongPoints = numWrongPoints * 1.0 / points.Count;

        int numTotallyWrongPoints = points.Count(p => !isOnEllipse(p.Point, ellipse, 10, 20));
        double ratioTotallyWrongPoints = numTotallyWrongPoints * 1.0 / points.Count;

        // this pseudo-distance is biased towards deviations on the major axis
        double pseudoDistance = Math.Sqrt(points.Sum(p => Math.Abs(1 - ellipseMetric(p.Point, ellipse))) / points.Count);

        // primarily take the number of points far from the elipse border as an error metric.
        // use pseudo-distance to break ties between elipses with the same number of wrong points
        return ratioWrongPoints * 1000  + ratioTotallyWrongPoints+ pseudoDistance / 1000;
    }


    /// <summary> shrink an ellipse if it is larger than the maximum grain dimensions </summary>
    private static CvBox2D rightSize(CvBox2D ellipse, List<EllipsePoint> points)
    {
        if (ellipse.Size.Width < MAX_WIDTH && ellipse.Size.Height < MAX_HEIGHT) return ellipse;

        // elipse is bigger than the maximum grain size
        // resize it so it fits, while keeping one edge of the bounding rectangle constant

        double desiredWidth = Math.Max(10, Math.Min(MAX_WIDTH, ellipse.Size.Width));
        double desiredHeight = Math.Max(10, Math.Min(MAX_HEIGHT, ellipse.Size.Height));

        CvPoint2D32f average = points.Average();

        // get the corners of the surrounding bounding box
        var corners = ellipse.BoxPoints().ToList();

        // find the corner that is closest to the center of mass of the points
        int i0 = ellipse.BoxPoints().Select((point, index) => new { point, index }).OrderBy(p => p.point.DistanceTo(average)).First().index;
        CvPoint p0 = corners[i0];

        // find the two corners that are neighbouring this one
        CvPoint p1 = corners[(i0 + 1) % 4];
        CvPoint p2 = corners[(i0 + 3) % 4];

        // p1 is the next corner along the major axis (widht), p2 is the next corner along the minor axis (height)
        if (p0.DistanceTo(p1) < p0.DistanceTo(p2))
        {
            CvPoint swap = p1;
            p1 = p2;
            p2 = swap;
        }

        // calculate the three other corners with the desired widht and height

        CvPoint2D32f edge1 = (p1 - p0);
        CvPoint2D32f edge2 = p2 - p0;
        double edge1Length = Math.Max(0.0001, p0.DistanceTo(p1));
        double edge2Length = Math.Max(0.0001, p0.DistanceTo(p2));

        CvPoint2D32f newCenter = (CvPoint2D32f)p0 + edge1 * (desiredWidth / edge1Length) + edge2 * (desiredHeight / edge2Length);

        CvBox2D smallEllipse = new CvBox2D(newCenter, new CvSize2D32f((float)desiredWidth, (float)desiredHeight), ellipse.Angle);

        return smallEllipse;
    }

    /// <summary> assure that the width of the elipse is the major axis, and the height is the minor axis.
    /// Swap widht/height and rotate by 90° otherwise  </summary>
    private static CvBox2D NormalizeEllipse(CvBox2D ellipse)
    {
        if (ellipse.Size.Width < ellipse.Size.Height)
        {
            ellipse = new CvBox2D(ellipse.Center, new CvSize2D32f(ellipse.Size.Height, ellipse.Size.Width), (ellipse.Angle + 90 + 360) % 360);
        }
        return ellipse;
    }

    /// <summary> greater than 1 for points outside ellipse, smaller than 1 for points inside ellipse </summary>
    private static double ellipseMetric(CvPoint p, CvBox2D ellipse)
    {
        double theta = ellipse.Angle * Math.PI / 180;
        double u = Math.Cos(theta) * (p.X - ellipse.Center.X) + Math.Sin(theta) * (p.Y - ellipse.Center.Y);
        double v = -Math.Sin(theta) * (p.X - ellipse.Center.X) + Math.Cos(theta) * (p.Y - ellipse.Center.Y);

        return u * u / (ellipse.Size.Width * ellipse.Size.Width / 4) + v * v / (ellipse.Size.Height * ellipse.Size.Height / 4);
    }

    /// <summary> Is the point on the ellipseBorder, within a certain tolerance </summary>
    private static bool isOnEllipse(CvPoint p, CvBox2D ellipse, double toleranceInner, double toleranceOuter)
    {
        double theta = ellipse.Angle * Math.PI / 180;
        double u = Math.Cos(theta) * (p.X - ellipse.Center.X) + Math.Sin(theta) * (p.Y - ellipse.Center.Y);
        double v = -Math.Sin(theta) * (p.X - ellipse.Center.X) + Math.Cos(theta) * (p.Y - ellipse.Center.Y);

        double innerEllipseMajor = (ellipse.Size.Width - toleranceInner) / 2;
        double innerEllipseMinor = (ellipse.Size.Height - toleranceInner) / 2;
        double outerEllipseMajor = (ellipse.Size.Width + toleranceOuter) / 2;
        double outerEllipseMinor = (ellipse.Size.Height + toleranceOuter) / 2;

        double inside = u * u / (innerEllipseMajor * innerEllipseMajor) + v * v / (innerEllipseMinor * innerEllipseMinor);
        double outside = u * u / (outerEllipseMajor * outerEllipseMajor) + v * v / (outerEllipseMinor * outerEllipseMinor);
        return inside >= 1 && outside <= 1;
    }


    /// <summary> count the number of foreground pixels for this grain </summary>
    public int CountPixel(CvMat img)
    {
        // todo: this is an incredibly inefficient way to count, allocating a new image with the size of the input each time
        using (CvMat mask = new CvMat(img.Rows, img.Cols, MatrixType.U8C1))
        {
            mask.SetZero();
            mask.FillPoly(new CvPoint[][] { this.EdgePoints.Select(p => p.Point).ToArray() }, CvColor.White);
            mask.And(img, mask);
            this.NumPixel = mask.CountNonZero();
        }
        return this.NumPixel;
    }

    /// <summary> draw the recognized shape of the grain </summary>
    public void Draw(CvMat img, CvColor color)
    {
        img.FillPoly(new CvPoint[][] { this.EdgePoints.Select(p => p.Point).ToArray() }, color);
    }

    /// <summary> draw the contours of the grain </summary>
    public void DrawContour(CvMat img, CvColor color)
    {
        img.DrawPolyLine(new CvPoint[][] { this.EdgePoints.Select(p => p.Point).ToArray() }, true, color);
    }

    /// <summary> draw the best-fit ellipse of the grain </summary>
    public void DrawEllipse(CvMat img, CvColor color)
    {
        img.DrawEllipse(this.Position, new CvSize2D32f(this.MajorRadius, this.MinorRadius), this.Angle, 0, 360, color, 1);
    }

    /// <summary> print the grain index and the number of pixels divided by the average grain size</summary>
    public void DrawText(double averageGrainSize, CvMat img, CvColor color)
    {
        img.PutText(String.Format("{0}|{1:0.0}", this.Index, this.NumPixel / averageGrainSize), this.Position + new CvPoint2D32f(-5, 10), font01, color);
    }

}

আমি এই সমাধানটি নিয়ে কিছুটা বিব্রত বোধ করি কারণ ক) আমি নিশ্চিত নই যে এটি এই চ্যালেঞ্জের চেতনার মধ্যে রয়েছে কিনা, এবং খ) কোডগল্ফ উত্তরের জন্য এটি খুব বড় এবং অন্যান্য সমাধানগুলির কমনীয়তার অভাব রয়েছে।

অন্যদিকে, আমি শস্যগুলি লেবেল করার ক্ষেত্রে যে অগ্রগতি অর্জন করেছি তাতে আমি যথেষ্ট খুশি , কেবল সেগুলি গণনা করি না, তাই রয়েছে।


আপনি জানেন যে ছোট নাম ব্যবহার করে এবং কিছু অন্যান্য গল্ফিং কৌশল প্রয়োগ করে আপনি সেই কোডের দৈর্ঘ্যকে
অপ্টিমাইজার

সম্ভবত, তবে আমি এই সমাধানটি আরও অবিস্মরণ করতে চাইনি। এটি আমার স্বাদের মতোই
উদাসীন

প্রয়াসের জন্য +1 এবং কারণ আপনি একমাত্র যা প্রতিটি শস্য পৃথকভাবে প্রদর্শন করার উপায় খুঁজে পান। দুর্ভাগ্যক্রমে কোডটি কিছুটা পুষ্পযুক্ত এবং হার্ডকোডযুক্ত ধ্রুবকগুলির উপর প্রচুর উপর নির্ভর করে। আমি যে স্ক্যানলাইন অ্যালগোরিদম লিখেছিলাম তা কীভাবে (অদৃশ্য রঙিন দানাগুলিতে) সম্পাদন করে তা জানতে আগ্রহী হবে।

আমি সত্যিই ভাবি যে এটি এই ধরণের সমস্যার জন্য সঠিক পদ্ধতি (আপনার জন্য +1), তবে একটি জিনিস আমি অবাক করে বলছি কেন আপনি "10 টি এলোমেলো প্রান্ত পিক্সেল চয়ন করেন", আমি মনে করি আপনি বেছে নিলে আপনি আরও ভাল পারফরম্যান্স পাবেন নিকটতম প্রান্ত পয়েন্টগুলির সর্বনিম্ন সংখ্যার প্রান্ত পয়েন্টগুলি (অর্থাত্ অংশগুলি যেগুলি আটকে থাকে), আমি ভাবব (তাত্ত্বিকভাবে) এটি "সহজতম" দানাটিকে প্রথমে নির্মূল করবে, আপনি কি এটি বিবেচনা করেছেন?
ডেভিড রজার্স

আমি এটি সম্পর্কে ভেবে দেখেছি, তবে এখনও চেষ্টা করে দেখিনি। '10 এলোমেলো শুরুর অবস্থান'টি একটি দেরী সংযোজন ছিল, যা যুক্ত করা সহজ এবং সমান্তরালভাবে সহজ। এর আগে 'সর্বদা শীর্ষে বাম কোণে' চেয়ে 'এক র্যান্ডম শুরুর অবস্থান' অনেক ভাল ছিল। প্রতিবার একই কৌশল দিয়ে শুরু করার অবস্থানগুলি বেছে নেওয়ার বিপদটি হ'ল আমি যখন সেরা ফিটটি সরিয়ে ফেলব তখন অন্য 9 জন সম্ভবত পরবর্তী সময় আবার বেছে নেওয়া হবে এবং সময়ের সাথে সাথে যারা শুরু হওয়া অবস্থানগুলির মধ্যে সবচেয়ে খারাপ তারা পিছনে থাকবে এবং আবার নির্বাচিত হবে এবং আবার। এমন একটি অংশ যা কেবলমাত্র অপূর্ণভাবে অপসারণ করা পূর্ববর্তী শস্যের অবশেষ হতে পারে।
হুগুরুন

17

সি ++, ওপেনসিভি, স্কোর: 9

আমার পদ্ধতির বুনিয়াদি ধারণাটি বেশ সহজ - চিত্র থেকে একক দানা (এবং "ডাবল শস্য" - 2 (তবে আরও নয়!) দানা মুছার চেষ্টা করুন এবং তারপরে ক্ষেত্রের উপর ভিত্তি করে পদ্ধতি ব্যবহার করে বাকী গণনা করুন (যেমন ফালকো, Ell এবং Belisarius)। এই পদ্ধতির ব্যবহারটি স্ট্যান্ডার্ড "এরিয়া পদ্ধতি" এর চেয়ে কিছুটা ভাল, কারণ ভাল গড়পড়তা পিক্সেলস পেরোবজেক্ট মান পাওয়া সহজ find

(প্রথম পদক্ষেপ) সবার আগে আমাদের এইচএসভিতে চিত্রের এস চ্যানেলটিতে ওতু বিয়ারাইজেশন ব্যবহার করা উচিত। পরবর্তী পদক্ষেপটি উত্তোলিত পূর্বভূমির মান উন্নত করতে ডিলিট অপারেটর ব্যবহার করছে। আমাদের কনট্যুরগুলি খুঁজে পাওয়া দরকার Than অবশ্যই কিছু রূপগুলি ধানের শীষ নয় - আমাদের খুব ছোট পাতাগুলি মুছে ফেলতে হবে (আয়তনের তুলনায় গড়পড়তা ছোট পিক্সেলপিরোবজেক্ট / 4 average গড়পিক্সেলপিক্সারপিরবজেক্ট আমার পরিস্থিতিতে 2855)। এখন অবশেষে আমরা শস্য গণনা শুরু করতে পারি :) (২ য় ধাপ) একক এবং দ্বিগুণ শস্য সন্ধান করা বেশ সহজ - নির্দিষ্ট রেঞ্জের ক্ষেত্রের ক্ষেত্রফলের জন্য সারসংক্ষেপের জন্য কেবল সূচির তালিকায় সন্ধান করুন - যদি কনট্যুর ক্ষেত্রটি সীমার মধ্যে থাকে তবে তালিকা থেকে মুছে ফেলুন এবং ১ যোগ করুন (বা 2 যদি এটি "ডাবল" শস্য হয়) শস্যের পাল্টাতে। (তৃতীয় পদক্ষেপ) শেষ পদক্ষেপটি অবশ্যই গড় পিক্সেলসপিরোবজেক্ট মান দ্বারা বাক্য রূপগুলির ক্ষেত্রফলকে ভাগ করে এবং শস্যের পাল্টাতে ফলাফল যুক্ত করে।

: (ইমেজ F.jpg জন্য) চিত্র এই ধারণা শব্দের চেয়ে ভাল দেখানো উচিত
: 1 ম ধাপ (ছোট contours এবং (গোলমাল) ছাড়া) প্রথম পদক্ষেপ (ছোট আউটপুট ছাড়াই (শব্দ))
- শুধুমাত্র সহজ contours এবং 2nd পদক্ষেপ: ২ য় পদক্ষেপ - কেবল সাধারণ প্রতিচ্ছবি
3 য় ধাপ - অবশিষ্ট contours এবং: তৃতীয় ধাপ - বাকী রূপগুলি

এখানে কোডটি দেওয়া হয়েছে এটি বরং কুরুচিপূর্ণ তবে কোনও সমস্যা ছাড়াই কাজ করা উচিত। অবশ্যই ওপেনসিভি দরকার।

#include "stdafx.h"

#include <cv.hpp>
#include <cxcore.h>
#include <highgui.h>
#include <vector>

using namespace cv;
using namespace std;

//A: 3, B: 5, C: 12, D: 25, E: 50, F: 83, G: 120, H:150, I: 151, J: 200
const int goodResults[] = {3, 5, 12, 25, 50, 83, 120, 150, 151, 200};
const float averagePixelsPerObject = 2855.0;

const int singleObjectPixelsCountMin = 2320;
const int singleObjectPixelsCountMax = 4060;

const int doubleObjectPixelsCountMin = 5000;
const int doubleObjectPixelsCountMax = 8000;

float round(float x)
{
    return x >= 0.0f ? floorf(x + 0.5f) : ceilf(x - 0.5f);
}

Mat processImage(Mat m, int imageIndex, int &error)
{
    int objectsCount = 0;
    Mat output, thresholded;
    cvtColor(m, output, CV_BGR2HSV);
    vector<Mat> channels;
    split(output, channels);
    threshold(channels[1], thresholded, 0, 255, CV_THRESH_OTSU | CV_THRESH_BINARY);
    dilate(thresholded, output, Mat()); //dilate to imporove quality of binary image
    imshow("thresholded", thresholded);
    int nonZero = countNonZero(output); //not realy important - just for tests
    if (imageIndex != -1)
        cout << "non zero: " << nonZero << ", average pixels per object: " << nonZero/goodResults[imageIndex] << endl;
    else
        cout << "non zero: " << nonZero << endl;

    vector<vector<Point>> contours, contoursOnlyBig, contoursWithoutSimpleObjects, contoursSimple;
    findContours(output, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //find only external contours
    for (int i=0; i<contours.size(); i++)
        if (contourArea(contours[i]) > averagePixelsPerObject/4.0)
            contoursOnlyBig.push_back(contours[i]); //add only contours with area > averagePixelsPerObject/4 ---> skip small contours (noise)

    Mat bigContoursOnly = Mat::zeros(output.size(), output.type());
    Mat allContours = bigContoursOnly.clone();
    drawContours(allContours, contours, -1, CV_RGB(255, 255, 255), -1);
    drawContours(bigContoursOnly, contoursOnlyBig, -1, CV_RGB(255, 255, 255), -1);
    //imshow("all contours", allContours);
    output = bigContoursOnly;

    nonZero = countNonZero(output); //not realy important - just for tests
    if (imageIndex != -1)
        cout << "non zero: " << nonZero << ", average pixels per object: " << nonZero/goodResults[imageIndex] << " objects: "  << goodResults[imageIndex] << endl;
    else
        cout << "non zero: " << nonZero << endl;

    for (int i=0; i<contoursOnlyBig.size(); i++)
    {
        double area = contourArea(contoursOnlyBig[i]);
        if (area >= singleObjectPixelsCountMin && area <= singleObjectPixelsCountMax) //is this contours a single grain ?
        {
            contoursSimple.push_back(contoursOnlyBig[i]);
            objectsCount++;
        }
        else
        {
            if (area >= doubleObjectPixelsCountMin && area <= doubleObjectPixelsCountMax) //is this contours a double grain ?
            {
                contoursSimple.push_back(contoursOnlyBig[i]);
                objectsCount+=2;
            }
            else
                contoursWithoutSimpleObjects.push_back(contoursOnlyBig[i]); //group of grainss
        }
    }

    cout << "founded single objects: " << objectsCount << endl;
    Mat thresholdedImageMask = Mat::zeros(output.size(), output.type()), simpleContoursMat = Mat::zeros(output.size(), output.type());
    drawContours(simpleContoursMat, contoursSimple, -1, CV_RGB(255, 255, 255), -1);
    if (contoursWithoutSimpleObjects.size())
        drawContours(thresholdedImageMask, contoursWithoutSimpleObjects, -1, CV_RGB(255, 255, 255), -1); //draw only contours of groups of grains
    imshow("simpleContoursMat", simpleContoursMat);
    imshow("thresholded image mask", thresholdedImageMask);
    Mat finalResult;
    thresholded.copyTo(finalResult, thresholdedImageMask); //copy using mask - only pixels whc=ich belongs to groups of grains will be copied
    //imshow("finalResult", finalResult);
    nonZero = countNonZero(finalResult); // count number of pixels in all gropus of grains (of course without single or double grains)
    int goodObjectsLeft = goodResults[imageIndex]-objectsCount;
    if (imageIndex != -1)
        cout << "non zero: " << nonZero << ", average pixels per object: " << (goodObjectsLeft ? (nonZero/goodObjectsLeft) : 0) << " objects left: " << goodObjectsLeft <<  endl;
    else
        cout << "non zero: " << nonZero << endl;
    objectsCount += round((float)nonZero/(float)averagePixelsPerObject);

    if (imageIndex != -1)
    {
        error = objectsCount-goodResults[imageIndex];
        cout << "final objects count: " << objectsCount << ", should be: " << goodResults[imageIndex] << ", error is: " << error <<  endl;
    }
    else
        cout << "final objects count: " << objectsCount << endl; 
    return output;
}

int main(int argc, char* argv[])
{
    string fileName = "A";
    int totalError = 0, error;
    bool fastProcessing = true;
    vector<int> errors;

    if (argc > 1)
    {
        Mat m = imread(argv[1]);
        imshow("image", m);
        processImage(m, -1, error);
        waitKey(-1);
        return 0;
    }

    while(true)
    {
        Mat m = imread("images\\" + fileName + ".jpg");
        cout << "Processing image: " << fileName << endl;
        imshow("image", m);
        processImage(m, fileName[0] - 'A', error);
        totalError += abs(error);
        errors.push_back(error);
        if (!fastProcessing && waitKey(-1) == 'q')
            break;
        fileName[0] += 1;
        if (fileName[0] > 'J')
        {
            if (fastProcessing)
                break;
            else
                fileName[0] = 'A';
        }
    }
    cout << "Total error: " << totalError << endl;
    cout << "Errors: " << (Mat)errors << endl;
    cout << "averagePixelsPerObject:" << averagePixelsPerObject << endl;

    return 0;
}

আপনি যদি সমস্ত পদক্ষেপের ফলাফল দেখতে চান তবে সমস্ত ইমোশো (.., ..) ফাংশন কলগুলি অবিরাম করুন এবং দ্রুত প্রসেসিং ভেরিয়েবলটিকে মিথ্যাতে সেট করুন। চিত্রসমূহ (A.jpg, B.jpg, ...) ডিরেক্টরি চিত্রে থাকা উচিত। বিকল্পভাবে অবশ্যই আপনি কমান্ড লাইন থেকে প্যারামিটার হিসাবে একটি চিত্রের নাম দিতে পারেন।

অবশ্যই যদি কিছু অস্পষ্ট থাকে তবে আমি এটিকে ব্যাখ্যা করতে পারি এবং / অথবা কিছু চিত্র / তথ্য সরবরাহ করতে পারি।


12

সি # + ওপেনসিভিশার্প, স্কোর: 71

এটি সবচেয়ে উদ্বেগজনক, আমি এমন একটি সমাধান পাওয়ার চেষ্টা করেছি যা জলাশয় ব্যবহার করে প্রতিটি শস্যকে আসলে চিহ্নিত করে , তবে আমি ঠিক। করতে পারেন না। পাওয়া. এটা। প্রতি. হবে।

আমি এমন একটি সমাধানের জন্য স্থির হয়েছি যা কমপক্ষে কিছু পৃথক শস্যকে পৃথক করে এবং তারপরে গড় দানার আকার নির্ধারণ করতে those শস্যগুলি ব্যবহার করে। তবে এখনও পর্যন্ত আমি হার্ডকডযুক্ত শস্যের আকার দিয়ে সমাধানগুলিকে পরাতে পারি না।

সুতরাং, এই সমাধানের মূল হাইলাইট: এটি শস্যের জন্য একটি নির্দিষ্ট পিক্সেল আকার ধারণ করে না, এবং ক্যামেরাটি সরানো থাকলেও বা ধানের ধরণ পরিবর্তন করা হলেও এটি কাজ করা উচিত।

A.jpg; শস্যের সংখ্যা: 3; প্রত্যাশিত 3; ত্রুটি 0; দানাদার প্রতি পিক্সেল: 2525,0;
B.jpg; শস্য সংখ্যা: 7; প্রত্যাশিত 5; ত্রুটি 2; দানাদার প্রতি পিক্সেল: 1920,0;
C.jpg; শস্য সংখ্যা: 6; প্রত্যাশিত 12; ত্রুটি 6; দানাদার প্রতি পিক্সেল: 4242,5;
d.jpg; শস্যের সংখ্যা: 23; প্রত্যাশিত 25; ত্রুটি 2; শস্য প্রতি পিক্সেল: 2415,5;
E.jpg; শস্যের সংখ্যা: 47; প্রত্যাশিত 50; ত্রুটি 3; শস্য প্রতি পিক্সেল: 2729,9;
F.jpg; শস্যের সংখ্যা: 65; প্রত্যাশিত 83; ত্রুটি 18; দানাদার প্রতি পিক্সেল: 2860,5;
G.jpg; শস্য সংখ্যা: 120; প্রত্যাশিত 120; ত্রুটি 0; দানাদার প্রতি পিক্সেল: 2552,3;
H.jpg; শস্যের সংখ্যা: 159; প্রত্যাশিত 150; ত্রুটি 9; শস্যের জন্য পিক্সেল: 2624,7;
i.jpg; শস্যের সংখ্যা: 141; প্রত্যাশিত 151; ত্রুটি 10; দানাদার প্রতি পিক্সেল: 2697,4;
J.jpg; শস্যের সংখ্যা: 179; প্রত্যাশিত 200; ত্রুটি 21; শস্য প্রতি পিক্সেল: 2847,1;
মোট ত্রুটি: 71

আমার সমাধান এর মতো কাজ করে:

চিত্রটি এইচএসভিতে রূপান্তর করে এবং স্যাচুরেশন চ্যানেলে ওটসু প্রান্তিককরণ প্রয়োগ করে অগ্রভাগটি পৃথক করুন । এটি খুব সাধারণভাবে, অত্যন্ত ভালভাবে কাজ করে এবং আমি যারা এই চ্যালেঞ্জটি চেষ্টা করতে চান তাদের প্রত্যেকের জন্য আমি এটির সুপারিশ করব:

saturation channel                -->         Otsu thresholding

এখানে চিত্র বর্ণনা লিখুন -> এখানে চিত্র বর্ণনা লিখুন

এটি পরিষ্কারভাবে ব্যাকগ্রাউন্ডটি সরিয়ে ফেলবে।

আমি তারপরে মান-চ্যানেলে একটি নির্দিষ্ট প্রান্তিক প্রয়োগ করে অগ্রভাগ থেকে শস্যের ছায়াগুলি সরিয়েছি । (নিশ্চিত না যে এটি আসলে অনেক সাহায্য করে তবে এটি যুক্ত করার পক্ষে যথেষ্ট সহজ ছিল)

এখানে চিত্র বর্ণনা লিখুন

তারপরে আমি অগ্রভাগের চিত্রটিতে একটি দূরত্বের রূপান্তর প্রয়োগ করি ।

এখানে চিত্র বর্ণনা লিখুন

এবং এই দূরত্বের রূপান্তরটিতে সমস্ত স্থানীয় ম্যাক্সিমা সন্ধান করুন।

এখানেই আমার ধারণাটি ভেঙে যায়। একই শস্যের মধ্যে একাধিক স্থানীয় ম্যাক্সিমায়া এড়াতে, আমাকে অনেকগুলি ফিল্টার করতে হবে। বর্তমানে আমি 45 পিক্সেলের ব্যাসার্ধের মধ্যে কেবল সবচেয়ে শক্তিশালী সর্বোচ্চ রাখি যার অর্থ প্রতিটি শস্যের স্থানীয় সর্বাধিক পরিমাণ থাকে না। এবং 45 পিক্সেলের ব্যাসার্ধের সত্যতা আমার কাছে নেই, এটি কেবল মূল্য হিসাবে কাজ করেছিল।

এখানে চিত্র বর্ণনা লিখুন

(যেমন আপনি দেখতে পাচ্ছেন, প্রতিটি শস্যের জন্য এটি যথেষ্ট পরিমাণে বীজ নয়)

তারপরে আমি সেই ম্যাক্সিমাকে বন্যার মতো অ্যালগরিদমের জন্য বীজ হিসাবে ব্যবহার করি:

এখানে চিত্র বর্ণনা লিখুন

ফলাফল মেহ । আমি বেশিরভাগ স্বতন্ত্র শস্যের জন্য আশা করছিলাম তবে ঝিঁঝিঁগুলি এখনও অনেক বড়।

এখন আমি ক্ষুদ্রতম ব্লবগুলি সনাক্ত করি, তাদের গড় পিক্সেল আকার গণনা করি এবং তারপরে শস্যগুলির সংখ্যা অনুমান করি। শুরুতে আমি যা করার পরিকল্পনা করছিলাম তা নয় , তবে এটি উদ্ধার করার একমাত্র উপায় ছিল।

সিস্টেম ব্যবহার করে ; সিস্টেম 
ব্যবহার করে সংগ্রহ জেনেরিক ; সিস্টেম 
ব্যবহার করে লিনক ; সিস্টেম 
ব্যবহার করে পাঠ্য ; ওপেনসিভিশার্প 
ব্যবহার করে ;

নামস্থান GrainTest2 { বর্গ প্রোগ্রাম { স্ট্যাটিক অকার্যকর প্রধান ( স্ট্রিং [] args ) { স্ট্রিং [] ফাইল = নতুন [] { "sourceA.jpg" , "sourceB.jpg" , "sourceC.jpg" , "sourceD.jpg" , " উত্সই.জেপিজি " , " উত্সএফ.জেপিজি " , " উত্সজি.জেপজি " , " উত্সএইচ.জেপজি " , " উত্সআই.জেপিজি " , " উত্সজে.জেপজি " , };int [] প্রত্যাশিত গ্রেইন

     
    
          
        
             
                               
                                     
                                     
                                      
                               
            = নতুন []} 3 , 5 , 12 , 25 , 50 , 83 , 120 , 150 , 151 , 200 ,};          

            int totalError = 0 ; int টোটালপিক্সেল = 0 ; 
             

            জন্য ( int- এ fileNo = 0 ; fileNo চিহ্নিতকারী = নতুন তালিকা ) (; 
                    ব্যবহার ( CvMemStorage স্টোরেজ = নতুন CvMemStorage ()) 
                    ব্যবহার ( CvContourScanner স্ক্যানার = নতুন CvContourScanner ( localMaxima , স্টোরেজ , CvContour sizeof , ContourRetrieval এক্সটার্নাল , ContourChain ApproxNone ))         
                    { // বীজ সংখ্যা 25, 35, 45, প্রতিটি স্থানীয় সর্বাধিক সেট করুন ... // (প্রকৃত সংখ্যা ব্যাপার না, PNG ভাল দৃশ্যমানতা জন্য মনোনীত) int- এ markerNo = 20 ; foreach ( CvSeq মধ্যে স্ক্যানার ) { 
                            markerNo + + = 5 ; 
                            মার্কার অ্যাড ( মার্কারনো ); 
                            waterShedMarkers ড্রকন্টারস ( সি , নতুন সিভিএসকালার ( মার্কারনো ), নতুন
                        
                        
                         
                         
                             সিভিএসকালার ( মার্কারনো ), 0 , - 1 ); } } 
                    ওয়াটারশেডমার্কারস সেভ ইমেজ ( "08-ওয়াটারশেড-বীজ.পিএনজি" );  
                        
                    


                    উত্স ওয়াটারশেড ( waterShedMarkers ); 
                    waterShedMarkers সেভ ইমেজ ( "09-ওয়াটারশেড-রেজাল্ট.পিএনজি" );


                    পিক্সেল তালিকা তালিকাভুক্ত করুন = নতুন তালিকা ();  

                    // Terrible hack because I could not get Cv2.ConnectedComponents to work with this openCv wrapper
                    // So I made a workaround to count the number of pixels per blob
                    waterShedMarkers.ConvertScale(waterShedThreshold);
                    foreach (int markerNo in markers)
                    {
                        using (CvMat tmp = new CvMat(waterShedMarkers.Rows, waterShedThreshold.Cols, MatrixType.U8C1))
                        {
                            waterShedMarkers.CmpS(markerNo, tmp, ArrComparison.EQ);
                            pixelsPerBlob.Add(tmp.CountNonZero());

                        }
                    }

                    // estimate the size of a single grain
                    // step 1: assume that the 10% smallest blob is a whole grain;
                    double singleGrain = pixelsPerBlob.OrderBy(p => p).ElementAt(pixelsPerBlob.Count/15);

                    // step2: take all blobs that are not much bigger than the currently estimated singel grain size
                    //        average their size
                    //        repeat until convergence (too lazy to check for convergence)
                    for (int i = 0; i  p  Math.Round(p/singleGrain)).Sum());

                    Console.WriteLine("input: {0}; number of grains: {1,4:0.}; expected {2,4}; error {3,4}; pixels per grain: {4:0.0}; better: {5:0.}", file, numGrains, expectedGrains[fileNo], Math.Abs(numGrains - expectedGrains[fileNo]), singleGrain, pixelsPerBlob.Sum() / 1434.9);

                    totalError += Math.Abs(numGrains - expectedGrains[fileNo]);
                    totalPixels += pixelsPerBlob.Sum();

                    // this is a terrible hack to visualise the estimated number of grains per blob.
                    // i'm too tired to clean it up
                    #region please ignore
                    using (CvMemStorage storage = new CvMemStorage())
                    using (CvMat tmp = waterShedThreshold.Clone())
                    using (CvMat tmpvisu = new CvMat(source.Rows, source.Cols, MatrixType.এস 8 সি 3 ))
                    {
                        foreach (int markerNo in markers)
                        {
                            tmp.SetZero();
                            waterShedMarkers.CmpS(markerNo, tmp, ArrComparison.EQ);
                            double curGrains = tmp.CountNonZero() * 1.0 / singleGrain;
                            using (
                                CvContourScanner scanner = new CvContourScanner(tmp, storage, CvContour.SizeOf, ContourRetrieval.External,
                                                                                ContourChain.ApproxNone))
                            {
                                tmpvisu.Set(CvColor.Random(), tmp);
                                foreach (CvSeq c in scanner)
                                {
                                    //tmpvisu.DrawContours(c, CvColor.Random(), CvColor.DarkGreen, 0, -1);
                                    tmpvisu.PutText("" + Math.Round(curGrains, 1), c.First().Value, new CvFont(FontFace.HersheyPlain, 2, 2),
                                                    CvColor.Red);
                                }

                            }


                        }
                        tmpvisu.SaveImage("10-visu.png");
                        tmpvisu.SaveImage("10-visu" + file + ".png");
                    }
                    #endregion

                }

            }
            Console.WriteLine("total error: {0}, ideal Pixel per Grain: {1:0.0}", totalError, totalPixels*1.0/expectedGrains.Sum());

        }
    }
}

হার্ড-কোডেড পিক্সেল-প্রতি-শস্য আকারের 2544.4 আকারের একটি ছোট পরীক্ষায় মোট 36 টির ত্রুটি দেখা গেছে, যা এখনও অন্যান্য সমাধানগুলির চেয়ে বড়।

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন


আমি মনে করি দূরত্ব পরিবর্তনের ফলে আপনি কিছুটা ছোট মান সহ প্রান্তিক ব্যবহার (ইরোড অপারেশনটিও দরকারী হতে পারে) ব্যবহার করতে পারেন - এটি শস্যের কয়েকটি গ্রুপকে ছোট ছোট দলে বিভক্ত করা উচিত (সাধারণত - শুধুমাত্র 1 বা 2 দানা দিয়ে)। Those সমস্ত নিঃসঙ্গ শস্যগুলি গণনা করা আরও সহজ হওয়া উচিত। বড় দলগুলি আপনি এখানে বেশিরভাগ লোক হিসাবে গণনা করতে পারেন - একক শস্যের গড় ক্ষেত্রফল দ্বারা ভাগ করে নেওয়া।
সিরিয়াল

9

এইচটিএমএল + জাভাস্ক্রিপ্ট: স্কোর 39

সঠিক মানগুলি হ'ল:

Estimated | Actual
        3 |      3
        5 |      5
       12 |     12
       23 |     25
       51 |     50
       82 |     83
      125 |    120
      161 |    150
      167 |    151
      223 |    200

এটি বৃহত্তর মানগুলিতে ভেঙে যায় (সঠিক নয়)।

window.onload = function() {
  var $ = document.querySelector.bind(document);
  var canvas = $("canvas"),
    ctx = canvas.getContext("2d");

  function handleFileSelect(evt) {
    evt.preventDefault();
    var file = evt.target.files[0],
      reader = new FileReader();
    if (!file) return;
    reader.onload = function(e) {
      var img = new Image();
      img.onload = function() {
        canvas.width = this.width;
        canvas.height = this.height;
        ctx.drawImage(this, 0, 0);
        start();
      };
      img.src = e.target.result;
    };
    reader.readAsDataURL(file);
  }


  function start() {
    var imgdata = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var data = imgdata.data;
    var background = 0;
    var totalPixels = data.length / 4;
    for (var i = 0; i < data.length; i += 4) {
      var red = data[i],
        green = data[i + 1],
        blue = data[i + 2];
      if (Math.abs(red - 197) < 40 && Math.abs(green - 176) < 40 && Math.abs(blue - 133) < 40) {
        ++background;
        data[i] = 1;
        data[i + 1] = 1;
        data[i + 2] = 1;
      }
    }
    ctx.putImageData(imgdata, 0, 0);
    console.log("Pixels of rice", (totalPixels - background));
    // console.log("Total pixels", totalPixels);
    $("output").innerHTML = "Approximately " + Math.round((totalPixels - background) / 2670) + " grains of rice.";
  }

  $("input").onchange = handleFileSelect;
}
<input type="file" id="f" />
<canvas></canvas>
<output></output>

ব্যাখ্যা: মূলত, চাল পিক্সেলের সংখ্যা গণনা করে এবং প্রতি শস্য গড় পিক্সেল দ্বারা এটি ভাগ করে দেয়।


3-ভাত চিত্র ব্যবহার করে, এটি আমার জন্য 0 অনুমান করেছে ...: /
Kroltan

1
@ ক্রোল্টন যখন আপনি সম্পূর্ণ আকারের চিত্র ব্যবহার করবেন না ।
ক্যালভিনের শখ

1
উইন্ডোতে ক্যালভিনের শখগুলি এফএফ 36 পুরো আকারের চিত্র সহ 0 পেয়েছে, উবুন্টু 3 পেল।
Kroltan

4
@ ববিজ্যাক ভাতটি ইমেজ জুড়ে কম-বেশি একই স্কেলের গ্যারান্টিযুক্ত। আমি এটি নিয়ে কোনও সমস্যা দেখছি না।
ক্যালভিনের শখ 16

1
@ গিথুব্যাগগোসাইট - একটি ব্যাখ্যা পুরোপুরি সুস্পষ্ট - যদি আপনি চিত্রের দ্বিবিকরণের ফলে সমস্ত সাদা পিক্সেল গণনা করেন এবং এই সংখ্যাটি শস্যের সংখ্যায় বিভক্ত করেন তবে আপনি এই ফল পাবেন। অবশ্যই সঠিক ফলাফল পৃথক হতে পারে কারণ ব্যবহৃত বাইনারিাইজেশন পদ্ধতি এবং অন্যান্য স্টাফগুলির কারণে (যেমন দ্বির্মুখীকরণের পরে পরিচালিত অপারেশনগুলি), তবে আপনি অন্যান্য উত্তরে দেখতে পাচ্ছেন এটি 2500-3500 এর মধ্যে থাকবে।
সাইরিয়েল

4

পিএইচপি দিয়ে একটি প্রচেষ্টা, সর্বনিম্ন স্কোরিং উত্তর নয় তবে এটির মোটামুটি সহজ কোড

স্কোর: 31

<?php
for($c = 1; $c <= 10; $c++) {
  $a = imagecreatefromjpeg("/tmp/$c.jpg");
  list($width, $height) = getimagesize("/tmp/$c.jpg");
  $rice = 0;
  for($i = 0; $i < $width; $i++) {
    for($j = 0; $j < $height; $j++) {
      $colour = imagecolorat($a, $i, $j);
      if (($colour & 0xFF) < 95) $rice++;
    }
  }
  echo ceil($rice/2966);
}

স্ব স্কোরিং

95 হল একটি নীল মান যা জিম্প 2966 দিয়ে টেস্ট করা যখন গড় দানা আকারের তখন কাজ করে বলে মনে হয়েছিল

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.